精通Go语言文件上传:深入探讨r.FormFile函数的应用与优化
1. 介绍
1.1 概述
在 Web 开发中,文件上传是一项常见的功能需求,用于允许用户向服务器提交文件,如图像、文档、视频等。Go 语言作为一门强大的服务器端编程语言,提供了方便且高效的方式来处理文件上传操作。其中,r.FormFile
函数是 Go 语言中处理 HTTP 请求中文件上传的关键函数之一。
1.2 r.FormFile 的作用
r.FormFile
函数用于从 HTTP 请求中获取上传的文件。它通常与 multipart/form-data
类型的表单一起使用,以解析用户提交的文件。该函数从请求体中解析并返回表单中指定名称的文件,并提供了文件的元数据和内容。通过使用 r.FormFile
函数,开发者可以轻松地处理文件上传过程,包括获取文件句柄、读取文件内容以及对文件进行进一步处理,如存储到服务器、处理文件内容等。因此,r.FormFile
函数在实现文件上传功能时具有重要作用。
2. r.FormFile 函数详解
2.1 函数签名
func (r *Request) FormFile(key string) (multipart.File, *multipart.FileHeader, error)
2.2 参数说明
r *Request
:表示 HTTP 请求对象,即客户端发送到服务器的 HTTP 请求。key string
:表示表单中文件上传字段的名称。
2.3 返回值
multipart.File
:表示文件的数据流。这个数据流可以被读取,用于进一步的处理,例如保存到本地文件或进行其他操作。*multipart.FileHeader
:表示文件的元数据,包括文件名、文件大小、文件类型等信息。error
:表示可能的错误。如果发生错误,将返回一个非 nil 的错误值;否则,返回 nil。
2.4 示例代码
以下是一个简单的示例代码,演示了如何使用 r.FormFile
函数从 HTTP 请求中获取上传的文件:
func uploadHandler(w http.ResponseWriter, r *http.Request) {// 获取上传的文件file, header, err := r.FormFile("file")if err != nil {// 处理错误http.Error(w, "Failed to retrieve file", http.StatusInternalServerError)return}defer file.Close()// 输出文件信息fmt.Fprintf(w, "Uploaded File: %+v\n", header.Filename)fmt.Fprintf(w, "File Size: %+v\n", header.Size)fmt.Fprintf(w, "MIME Type: %+v\n", header.Header.Get("Content-Type"))// 其他操作,例如保存文件到服务器
}
在上面的示例中,我们使用 r.FormFile
函数从 HTTP 请求中获取名为 "file"
的上传文件。如果成功获取文件,则会返回文件的数据流 file
和文件的元数据 header
。我们可以通过 header
获取文件名、文件大小、文件类型等信息,然后进行进一步的处理,例如输出文件信息或保存文件到服务器。
3. 使用 r.FormFile 处理文件上传
3.1 单文件上传示例
在单文件上传示例中,我们演示了如何使用 r.FormFile
函数处理单个文件上传的情况。
func uploadHandler(w http.ResponseWriter, r *http.Request) {// 解析上传的文件file, header, err := r.FormFile("file")if err != nil {// 处理文件上传失败的错误http.Error(w, "Failed to retrieve file", http.StatusInternalServerError)return}defer file.Close()// 输出文件信息fmt.Fprintf(w, "Uploaded File: %+v\n", header.Filename)fmt.Fprintf(w, "File Size: %+v\n", header.Size)fmt.Fprintf(w, "MIME Type: %+v\n", header.Header.Get("Content-Type"))// 其他操作,例如保存文件到服务器
}
在上面的示例中,我们使用 r.FormFile
函数从 HTTP 请求中获取名为 "file"
的上传文件。如果成功获取文件,则会返回文件的数据流 file
和文件的元数据 header
。我们可以通过 header
获取文件名、文件大小、文件类型等信息,然后进行进一步的处理,例如输出文件信息或保存文件到服务器。
3.2 多文件上传示例
对于多文件上传,我们可以在表单中定义多个文件上传字段,然后分别使用 r.FormFile
函数处理每个字段的文件上传。
func uploadHandler(w http.ResponseWriter, r *http.Request) {// 解析上传的文件r.ParseMultipartForm(10 << 20) // 限制内存使用不超过 10MB// 处理多个文件上传字段for key, files := range r.MultipartForm.File {for _, fileHeader := range files {// 获取上传文件file, err := fileHeader.Open()if err != nil {// 处理文件上传失败的错误http.Error(w, "Failed to retrieve file", http.StatusInternalServerError)return}defer file.Close()// 输出文件信息fmt.Fprintf(w, "Uploaded File: %+v\n", fileHeader.Filename)fmt.Fprintf(w, "File Size: %+v\n", fileHeader.Size)fmt.Fprintf(w, "MIME Type: %+v\n", fileHeader.Header.Get("Content-Type"))// 其他操作,例如保存文件到服务器}}
}
在上面的示例中,我们使用了 r.ParseMultipartForm
函数来解析表单中的多个文件上传字段,并限制内存使用量不超过 10MB。然后,我们使用 r.MultipartForm.File
字段遍历每个文件上传字段,分别处理每个字段中的文件上传。
3.3 错误处理
在处理文件上传过程中,我们需要注意错误处理,以确保应用程序的稳定性。对于文件上传失败等错误情况,我们需要适当地处理,并向客户端返回合适的错误消息。
func uploadHandler(w http.ResponseWriter, r *http.Request) {// 解析上传的文件file, header, err := r.FormFile("file")if err != nil {// 处理文件上传失败的错误http.Error(w, "Failed to retrieve file", http.StatusInternalServerError)return}defer file.Close()// 其他操作
}
在上面的示例中,我们使用 if err != nil
来检查是否有错误发生,并在出现错误时返回相应的 HTTP 错误码给客户端。这有助于调试问题,并使客户端能够得到合适的反馈。
4. 与其他文件上传函数的比较
4.1 r.FormFile 与 r.MultipartReader 的比较
-
r.FormFile:
- 适用于简单的文件上传场景,方便快捷。
- 可以直接从 HTTP 请求中获取文件句柄和文件元数据,使用简单。
- 适合处理单个文件上传的情况,对于多文件上传则需要遍历表单中的每个文件上传字段。
- 在处理大文件上传时可能会有内存开销,因为文件数据会被存储在内存中。
-
r.MultipartReader:
- 更灵活,适用于复杂的文件上传场景。
- 可以手动解析 HTTP 请求体,逐个获取文件句柄和文件元数据,更加灵活。
- 可以自定义处理文件上传过程,例如并发处理、自定义内存限制等。
- 对于大文件上传或者需要更精细控制的情况下,可以更好地控制内存使用。
4.2 与第三方包的比较
Go 社区中还有一些第三方包可以用于处理文件上传,例如 github.com/julienschmidt/httprouter
、github.com/gin-gonic/gin
等。
- r.FormFile 与第三方包的比较:
r.FormFile
是 Go 标准库提供的文件上传函数,使用简单,不需要引入额外的依赖。- 第三方包提供了更多的功能和选项,例如自定义中间件、更丰富的路由功能等。
- 根据项目需求和个人偏好,可以选择使用标准库的
r.FormFile
函数或者第三方包来处理文件上传。
总的来说,对于简单的文件上传需求,使用标准库的 r.FormFile
函数是一个不错的选择;而对于复杂的文件上传场景,可以考虑使用第三方包或者更底层的 r.MultipartReader
来实现更灵活的文件上传功能。
5. 安全性考虑
在处理文件上传时,确保应用程序的安全性至关重要。以下是几个安全性考虑方面:
5.1 文件类型验证
文件类型验证是确保上传的文件是安全的一种重要方式。通过验证文件的 MIME 类型或文件扩展名,可以防止用户上传恶意文件,例如执行恶意代码的脚本文件或包含病毒的文件。
func uploadHandler(w http.ResponseWriter, r *http.Request) {// 解析上传的文件file, header, err := r.FormFile("file")if err != nil {// 处理文件上传失败的错误http.Error(w, "Failed to retrieve file", http.StatusInternalServerError)return}defer file.Close()// 获取文件的 MIME 类型contentType := header.Header.Get("Content-Type")// 验证文件类型if !isValidFileType(contentType) {http.Error(w, "Invalid file type", http.StatusBadRequest)return}// 其他操作,例如保存文件到服务器
}
在上面的示例中,我们通过 header.Header.Get("Content-Type")
获取了文件的 MIME 类型,并使用自定义的 isValidFileType
函数进行验证。根据应用程序的需求,可以定义一个白名单来限制允许上传的文件类型。
5.2 文件大小限制
限制文件大小可以防止用户上传过大的文件,从而保护服务器免受攻击或耗尽资源。可以设置最大文件大小限制,并在上传文件之前进行验证。
const maxFileSize = 10 << 20 // 10MBfunc uploadHandler(w http.ResponseWriter, r *http.Request) {// 解析上传的文件file, header, err := r.FormFile("file")if err != nil {// 处理文件上传失败的错误http.Error(w, "Failed to retrieve file", http.StatusInternalServerError)return}defer file.Close()// 验证文件大小if header.Size > maxFileSize {http.Error(w, "File size exceeds the limit", http.StatusRequestEntityTooLarge)return}// 其他操作,例如保存文件到服务器
}
在上面的示例中,我们定义了一个最大文件大小 maxFileSize
,并在上传文件之前检查文件大小是否超过了限制。
5.3 防止文件覆盖攻击
文件覆盖攻击是指攻击者试图利用文件上传功能覆盖系统中的重要文件。为了防止文件覆盖攻击,应该采用安全的文件命名策略,并在保存文件之前检查目标文件是否已经存在。
func uploadHandler(w http.ResponseWriter, r *http.Request) {// 解析上传的文件file, header, err := r.FormFile("file")if err != nil {// 处理文件上传失败的错误http.Error(w, "Failed to retrieve file", http.StatusInternalServerError)return}defer file.Close()// 生成安全的文件名safeFileName := generateSafeFileName(header.Filename)// 检查目标文件是否已经存在if _, err := os.Stat(safeFileName); err == nil {http.Error(w, "File already exists", http.StatusConflict)return}// 其他操作,例如保存文件到服务器
}
在上面的示例中,我们使用 generateSafeFileName
函数生成安全的文件名,并在保存文件之前检查目标文件是否已经存在。这样可以避免文件覆盖攻击的风险。
6. 性能优化建议
6.1 合理设置 maxMemory 参数
ParseMultipartForm
函数的 maxMemory
参数用于限制解析 multipart/form-data
请求时的内存使用量。合理设置 maxMemory
参数可以避免内存溢出的问题,并提高应用程序的性能。通常情况下,应根据应用程序的需求和预期的文件上传大小,设置一个适当的值。对于大文件上传,可以将 maxMemory
参数设为一个较小的值,以便将大部分文件数据保存到临时文件中,从而节省内存。
// 设置最大内存使用量为 20MB
maxMemory := int64(20 << 20) // 20MB
r.ParseMultipartForm(maxMemory)
6.2 使用临时文件处理大文件上传
对于大文件上传,将文件数据保存到内存中可能会导致内存消耗过大,从而影响应用程序的性能和稳定性。为了优化性能,可以将大文件数据保存到临时文件中,而不是全部存储在内存中。这可以通过合理设置 maxMemory
参数来实现,以及使用临时文件来处理大文件上传。
// 设置最大内存使用量为 0,将所有文件数据保存到临时文件中
r.ParseMultipartForm(0)
6.3 并发处理文件上传
在处理大量并发的文件上传请求时,可以考虑使用并发处理的方式来提高性能和吞吐量。通过使用 Go 语言的并发机制,例如 goroutines 和 channels,可以实现并发处理文件上传。可以将文件上传任务分配给不同的 goroutines,并使用适当的同步机制来协调它们的执行。
// 使用 goroutines 并发处理文件上传任务
go func() {// 处理文件上传逻辑
}()
通过以上的性能优化建议,可以有效地提高文件上传过程中的性能和稳定性,特别是在处理大文件上传和大量并发上传请求时。
7. 总结
文件上传是 Web 开发中常见的功能之一,而在 Go 语言中,通过使用 r.FormFile
函数可以方便地处理文件上传。本文深入探讨了 r.FormFile
函数的用法、安全性考虑以及性能优化建议,以帮助开发者更好地应用于实际项目中。
通过 r.FormFile
函数,我们可以轻松地从 HTTP 请求中获取上传的文件,并进行进一步的处理,例如保存到服务器、读取文件内容等。同时,我们也强调了安全性的重要性,包括文件类型验证、文件大小限制以及防止文件覆盖攻击等方面。这些安全性考虑可以保护应用程序免受恶意文件上传的影响,确保系统安全稳定运行。
另外,本文还提供了性能优化建议,包括合理设置 maxMemory
参数、使用临时文件处理大文件上传以及并发处理文件上传等方面。这些优化建议可以提高文件上传过程中的性能和吞吐量,确保应用程序能够高效地处理文件上传请求。
总而言之,掌握 r.FormFile
函数的使用方法,并结合安全性考虑和性能优化策略,可以帮助开发者更好地实现文件上传功能,并提高应用程序的质量和性能。希望本文能为开发者在文件上传方面的工作提供一些有价值的指导和帮助。
作者信息 作者 : 繁依Fanyi CSDN: https://techfanyi.blog.csdn.net 掘金:https://juejin.cn/user/4154386571867191 |
相关文章:
精通Go语言文件上传:深入探讨r.FormFile函数的应用与优化
1. 介绍 1.1 概述 在 Web 开发中,文件上传是一项常见的功能需求,用于允许用户向服务器提交文件,如图像、文档、视频等。Go 语言作为一门强大的服务器端编程语言,提供了方便且高效的方式来处理文件上传操作。其中,r.F…...
【C语言】字符串
C语言用字符数组存放字符串,字符数组中的各元素依次存放字符串的各字符 一维字符数组:存放一个字符串(每个数组元素存放一个字符)二维字符数组:存放多个一维数组(字符串);二维数组的…...
云计算探索-DAS、NAS与SAN存储技术演进及其应用比较
1,介绍 随着信息技术的飞速发展,数据存储的需求日益增长,各种存储技术也应运而生。在众多的存储解决方案中,直接附加存储(Direct Attached Storage,简称DAS)、网络附加存储(Network …...
手机有线投屏到直播姬pc端教程
1 打开哔哩哔哩直播姬客户端并登录(按下图进行操作) 2 手机用usb数据线连接电脑(若跳出安装驱动的弹窗点击确定或允许),usb的连接方式为仅充电(手机差异要求为仅充电),不同品牌手机要求可能不一样,根据实际的来 3 在投屏过程中不要更改usb的连接方式(不然电脑会死机需要重启) …...
SOA、分布式、微服务之间的关系?
分布式它本身就是一种系统部署的架构理念,意思就是将一个系统拆分为各个部分,然后分别部署到不同的机器上去,SOA和微服务项目的部署方式都可以是分布式架构。 而SOA和微服务它们都是面向服务的架构,但是微服务相比于SOA在服务粒度…...
Java多线程学习(概念笔记)
面试题:并行和并发有什么区别? 现在都是多核CPU,在多核CPU下 并发是同一时间应对多件事情的能力,多个线程轮流使用一个或多个CPU 并行是同一时间动手做多件事情的能力,4核CPU同时执行4个线程 面试题:创建线…...
【C++】set和map
set和map就是我们上篇博客说的key模型和keyvalue模型。它们属于是关联式容器,我们之前说过普通容器和容器适配器,这里的关联式容器就是元素之间是有关联的,通过上篇博客的讲解我们也对它们直接的关系有了一定的了解,那么下面我们先…...
yolov5 v7.0打包exe文件,使用C++调用
cd到yolo5文件夹下 pyinstaller -p 当前路径 -i logo图标 detect.py问题汇总 运行detect.exe找不到default.yaml 这个是yolov8里的文件 1 复制权重文件到exe所在目录。 2 根据报错提示的配置文件路径,把default.yaml复制放到相应的路径下。(缺少相应…...
保研线性代数机器学习基础复习2
1.什么是群(Group)? 对于一个集合 G 以及集合上的操作 ,如果G G-> G,那么称(G,)为一个群,并且满足如下性质: 封闭性:结合性:中性…...
vultr ubuntu 服务器远程桌面安装及连接
一. 概述 vultr 上开启一个linux服务器,都是以终端形式给出的,默认不带 ui 桌面的,那其实对于想使用服务器上浏览器时的情形不是很好。那有没有方法在远程服务器安装桌面,然后原程使用呢?至少ubuntu的服务器是有的&am…...
前端学习<二>CSS基础——12-CSS3属性详解:动画详解
前言 本文主要内容: 过渡:transition 2D 转换 transform 3D 转换 transform 动画:animation 过渡:transition transition的中文含义是过渡。过渡是CSS3中具有颠覆性的一个特征,可以实现元素不同状态间的平滑过渡…...
Sqoop 的安装与配置
目录 1 下载并解压2 修改配置文件3 添加环境变量4 拷贝 JDBC 驱动5 测试Sqoop是否能够成功连接数据库 下载地址 1 下载并解压 (1)上传安装包 sqoop-1.4.6.bin__hadoop-2.0.4-alpha.tar.gz 到 hadoop101 的 /opt/software 路径中 (2…...
Mysql设置访问权限(docker配置)
1.运行命令:docker exec -it 数据库名 bash,我这里是bot_test, docker exec -it bot_test bash 2.运行命令mysql -uroot -p --default-character-setutf8,输入密码连接数据库 3.运行命令show databases,查看当前的表 4.进入my…...
【Linux】详解软硬链接
一、软硬链接的建立方法 1.1软链接的建立 假设在当前目录下有一个test.txt文件,要对其建立软链接,做法如下: ln就是link的意思,-s表示软链接,test.txt要建立软链接的文件名,后面跟上要建立的软链接文件名…...
维修贝加莱4PP420.1043-B5触摸屏Power Panel 400工业电脑液晶
深圳捷达工控维修为贝加莱、HMI 显示电源面板 400 4PP420.1043-B5 提供专业电子维修。在 深圳捷达工控维修,我们拥有及时且经济高效地维修 B&R 、HMI Display Power Panel 400 4PP420.1043-B5 的经验。我们为发送给我们工厂维修的贝加莱 HMI 显示面板 400 4PP42…...
Java_21 完成一半题目
完成一半题目 有 N 位扣友参加了微软与力扣举办了「以扣会友」线下活动。主办方提供了 2*N 道题目,整型数组 questions 中每个数字对应了每道题目所涉及的知识点类型。 若每位扣友选择不同的一题,请返回被选的 N 道题目至少包含多少种知识点类型。 示例…...
【WPF应用21】WPF 中的 TextBox 控件详解与示例
在 Windows Presentation Foundation (WPF) 中,TextBox 控件是一个强大的输入控件,允许用户输入、编辑和选择文本。TextBox 控件在各种应用程序中都非常常见,例如表单、对话框和编辑器。本文将详细介绍 TextBox 控件的功能、使用方法、属性、…...
小程序页面传参?
小程序页面之间传递参数通常可以通过以下几种方式实现: 通过 URL 参数传递:可以在跳转目标页面时,在 URL 中添加参数,目标页面可以通过 options 参数获取传递过来的数据。 // 页面 A wx.navigateTo({url: targetPage?param1value…...
C++list的模拟实现
为了实现list,我们需要实现三个类 一、List的节点类 template<class T> struct ListNode {ListNode(const T& val T()):_pPre(nullptr),_pNext(nullptr),_val(val){}ListNode<T>* _pPre;ListNode<T>* _pNext;T _val; }; 二、List的迭代器…...
Leetcode 187. 重复的DNA序列
DNA序列 由一系列核苷酸组成,缩写为 ‘A’, ‘C’, ‘G’ 和 ‘T’.。 例如,“ACGAATTCCG” 是一个 DNA序列 。 在研究 DNA 时,识别 DNA 中的重复序列非常有用。 给定一个表示 DNA序列 的字符串 s ,返回所有在 DNA 分子中出现不…...
都江堰泛计算操作系统(多机)应用方向
1、异构多核芯片 DJYOS是全球唯一支持异构多核的操作系统。当前的系统级芯片,为了适应多样化的用户需求,基本上都采用了异构多核架构。传统操作系统就需要在一个芯片内,运行多种操作系统,开发工作更加复杂,运行协同性低…...
【第十二届“泰迪杯”数据挖掘挑战赛】【2024泰迪杯】B题基于多模态特征融合的图像文本检索—解题全流程(论文更新)
【第十二届“泰迪杯”数据挖掘挑战赛】【2024泰迪杯】B题基于多模态特征融合的图像文本检索更新(论文更新) 本节主要更新了论文、训练日志的log数据提取(Loss、ACC、RK)等数据可视化作图的代码 B题交流QQ群: 4583…...
蓝桥杯22年第十三届省赛-统计子矩阵|一维前缀和加双指针
题目链接: 1.统计子矩阵 - 蓝桥云课 (lanqiao.cn) 蓝桥杯2022年第十三届省赛真题-统计子矩阵 - C语言网 (dotcpp.com) 说明: 涉及到子矩阵的时候,一般就跟前缀和相关,可以降维。 先回顾一下最大子矩阵,回忆一下一…...
SaaS 电商设计 (十) 记一次 5000kw 商品数据ES迁移 (详细的集群搭建以及线上灰度过程设计)
目录 一.背景二.技术目标三.技术方案3.1 整体流程3.2 ES 切换前:完成整体新集群的搭建.i:拓扑结构设计ii: 如何选择整体的 **ES** 集群配置. 3.3 **ES** 版本切换中3.3.1 多client版本兼容3.3.2 Router的设计 3.4 ES 切换后3.5 开箱即用 四.总结 专栏系列 -SaaS 电商设计 (一) …...
linux安装Tomcat
linux安装Tomcat 下载地址:https://archive.apache.org/dist/tomcat/tomcat-8/v8.5.87/bin/apache-tomcat-8.5.87.tar.gz 将下载的安装包传到该文件夹 解压安装包 tar -zxvf apache-tomcat-8.5.87.tar.gz 配置环境变量 vim /etc/profile 添加指定文件最下方 expo…...
【机器学习300问】57、机器是如何读得懂文本数据的呢?
你知道的,人工智能的大佬们想方设法的让机器具备人一样的能力,比如读懂文本的能力。既然机器是在模仿人类,那么问题“机器是如何读得懂文本数据的呢?”就可以变成“人是如何读得懂文本数据的呢?” 一、人是如何读得懂…...
了解XSS和CSRF攻击与防御
什么是XSS攻击 XSS(Cross-Site Scripting,跨站脚本攻击)是一种常见的网络安全漏洞,它允许攻击者在受害者的浏览器上执行恶意脚本。这种攻击通常发生在 web 应用程序中,攻击者通过注入恶意脚本来利用用户对网站的信任&…...
NEO 学习之 MLE(最大似然估计)
文章目录 简单题目MLE 在不同的分布的运用正态分布指数分布均匀分布泊松分布 如何理解 最大似然估计? 就是我们先取出一堆样本,得到一个L( θ \theta θ) 函数,然后的话,这个是关于 θ \theta θ 的一个函数,那么由于存…...
going和Java对比有什么不同
语法风格:Golang 和 Java 的语法风格有很大的不同。Golang 更加简单,语法类似于 C 语言,而 Java 比较复杂,语法类似于 C。 并发:Golang 在并发方面有很大的优势,支持轻量级线程 goroutine 和 channel 通信…...
RabbitMQ面经 手打浓缩版
保证可靠性 生产者 本地事务完成和消息发送同时完成 通过事务消息完成 重写confirm在里面做逻辑处理 确保发送成功(不成功就放入到重试队列) MQ 打开持久化确保消息不会丢失 消费者 改成手动回应 不重复消费 生产者 保证不重复发送消息 消费者…...
建设电影网站选服务器怎么选/河南省郑州市金水区
模型矩阵 观察矩阵 投影矩阵 奠定了三维理论的基础。 vertex data(顶点位置数据 x,y,z)经过 model matrix(模型矩阵)变换,即对顶点位置数据 x,y,z 进行平移、旋转、缩放,得到新的顶点位置,然后根…...
正规的网店平台有哪些/网站如何提升seo排名
torch.manual_seed(seed)设定生成随机数的种子,并返回一个torch._C.Generator对象,参数:seed(int or long):种子。torch.initial_seed()返回生成随机数的原始种子值(python long)。torch.get_rng_state() 返回随机生成…...
白云区网站建设/网络营销论文
摘 要 在农业生产活动中,温度和湿度是影响农作物生长的重要因素。因此,对农作物生长的温湿度监测显得尤为重要。随着互联网技术的飞速发展,“互联网农业”已成为促进农业规模化生产的新动力,采用“互联网农业”的生产模式能有效的…...
可以做外链的网站/南京百度推广
1:场景一:工厂模式中简单工厂模式优化 1)简单工厂模式也可以叫做静态方法模式(因为工厂类一般都是在内部定义了一个静态方法) 2)简单工厂模式通过创建一个对应的工厂类,将类实例化的操作与使用…...
成都市区必去的景点/南宁优化网站网络服务
先解释下标题的意思: 就是点击上图中的上传图片之后,出现这个文件选择框的反应时间太长,IE和火狐浏览器还正常点。谷歌和360等浏览器一般要8秒左右才能打开,用户体验太差了。 这里我们要解决这个问题首先就得知道简单的文件上传的…...
wordpress 二维码插件下载地址/北京网站优化培训
Springboot整合mybatisPlusmysqldruidswaggerUI mqtt整合mqtt整合druid整合mybatis-plus完整pom完整yml整合swaggerUi整合log4jMQTT 物联网系统基本架构本物联网系列mqtt)整合mqtt <!--mqtt依赖--><dependency><groupId>org.springframework.boot</groupI…...