上传和下载文件
上传文件
- 服务端使用
ctx.SaveUploadedFile
保存文件到服务器
Go
// 通过name获取form file
func (c *Context) FormFile(name string) (*multipart.FileHeader, error)
// 获取上传的整个form
func (c *Context) MultipartForm() (*multipart.Form, error)
// 保存文件
func (c *Context) SaveUploadedFile(file *multipart.FileHeader, dst string) error
TIP
MaxMultipartMemory
参数用于设置内存缓冲区的最大容量
,也就是上传的数据超过该容量时,将会把文件流写入磁盘中,而不是一直保留在内存中。这是为了防止攻击者发送大量数据导致服务器内存耗尽,从而引发拒绝服务攻击(DoS)。8 << 20
表示将数字8左移20位
,即将数字8
转换为字节为单位的容量值,得到的结果是8MB
上传单个文件
解析上传单个文件参数使用
ctx.FormFile
,获取到的就是FormData
中的file
字段对应的文件获取
FormData
中其他字段使用ctx.PostForm
后端逻辑
Go
// 上传单个文件
func singleFileHandler(ctx *gin.Context) {
name := ctx.PostForm("name")
force := ctx.PostForm("force")
file, err := ctx.FormFile("file")
if err != nil {
fmt.Println("ctx.FormFile err", err)
}
savePath := path.Join("uploads", file.Filename)
fmt.Println(name, file.Filename, file.Size, "============")
if err := ctx.SaveUploadedFile(file, savePath); err != nil {
ctx.JSON(http.StatusOK, gin.H{
"code": -1,
"msg": "文件上传失败",
"data": nil,
})
return
}
ctx.JSON(http.StatusOK, gin.H{
"code": 0,
"msg": "文件上传成功",
"data": gin.H{"filepath": savePath, "name": name, "force": force},
})
}
func main() {
router := gin.Default()
//MaxMultipartMemory 参数用于设置内存缓冲区的最大容量,也就是上传的数据超过该容量时,将会把文件流写入磁盘中,而不是一直保留在内存中。这是为了防止攻击者发送大量数据导致服务器内存耗尽,从而引发拒绝服务攻击(DoS)。
// 8 << 20 表示将数字 8 左移 20 位,即将数字 8 转换为字节为单位的容量值,得到的结果是 8 MB
router.MaxMultipartMemory = 8 << 20 // 8M
router.Static("/static", "assets")
router.Static("/tpl", "templates")
router.POST("/single_file", singleFileHandler)
router.Run(":9100")
}
前端逻辑
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>ajax</title>
</head>
<body>
<div class="upload-wrapper">
<input id="ajax" type="file" multiple>
<br>
<br>
<button id="btn-submit">提交</button>
</div>
<script>
const btn = document.getElementById('btn-submit')
btn.addEventListener('click', handleUpload)
function handleUpload() {
const inputFile = document.getElementById('ajax'),
fileList = inputFile.files
if (!fileList.length) {
return alert('请选择文件')
}
/* 使用FormData对象,将上传对象存入FormData对象内 */
const formData = new FormData()
formData.append("name", "single file")
formData.append("force", "true")
for (let i = 0; i < fileList.length; i++) {
formData.append('file', fileList[i])
}
const xhr = new XMLHttpRequest()
xhr.open('POST', 'http://localhost:9100/single_file', true)
xhr.onreadystatechange = function() {
if(xhr.readyState === 4 && xhr.status === 200) {
const obj = JSON.parse(xhr.responseText);
console.log(obj);
}
}
xhr.send(formData)
}
</script>
</body>
</html>
如下图所示:
上传多个文件
- 使用
ctx.MultipartForm
获取多个FormData
数据
后端逻辑:
Go
// 上传多个文件
func multiFileHandler(ctx *gin.Context) {
multipleName := ctx.PostForm("name")
form, err := ctx.MultipartForm()
if err != nil {
fmt.Println("ctx.MultipartForm err =>>>", err)
}
files := form.File["file"]
var paths []string
for _, file := range files {
savePath := path.Join("uploads", file.Filename)
if err := ctx.SaveUploadedFile(file, savePath); err != nil {
ctx.JSON(http.StatusOK, gin.H{
"code": -1,
"msg": "文件上传失败",
"data": nil,
})
return
}
paths = append(paths, savePath)
}
ctx.JSON(http.StatusOK, gin.H{
"code": 0,
"msg": "文件上传成功",
"data": gin.H{
"paths": paths,
"name": multipleName,
},
})
}
func main() {
router := gin.Default()
//MaxMultipartMemory 参数用于设置内存缓冲区的最大容量,也就是上传的数据超过该容量时,将会把文件流写入磁盘中,而不是一直保留在内存中。这是为了防止攻击者发送大量数据导致服务器内存耗尽,从而引发拒绝服务攻击(DoS)。
// 8 << 20 表示将数字 8 左移 20 位,即将数字 8 转换为字节为单位的容量值,得到的结果是 8 MB
router.MaxMultipartMemory = 8 << 20 // 8M
router.Static("/static", "assets")
router.Static("/tpl", "templates")
router.POST("/multi_file", multiFileHandler)
router.Run(":9100")
}
前端逻辑
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>ajax</title>
</head>
<body>
<div class="upload-wrapper">
<input id="ajax" type="file" multiple>
<br>
<br>
<button id="btn-submit">提交</button>
</div>
<script>
const btn = document.getElementById('btn-submit')
btn.addEventListener('click', handleUpload)
function handleUpload() {
const inputFile = document.getElementById('ajax'),
fileList = inputFile.files
if (!fileList.length) {
return alert('请选择文件')
}
/* 使用FormData对象,将上传对象存入FormData对象内 */
const formData = new FormData()
formData.append("name", "single file")
for (let i = 0; i < fileList.length; i++) {
formData.append('file', fileList[i])
}
const xhr = new XMLHttpRequest()
xhr.open('POST', 'http://localhost:9100/multi_file', true)
xhr.onreadystatechange = function() {
if(xhr.readyState === 4 && xhr.status === 200) {
const obj = JSON.parse(xhr.responseText);
console.log(obj);
}
}
xhr.send(formData)
}
</script>
</body>
</html>
如图所示:
文件下载
Go
// 设置响应问见是文件流模式,可以让浏览器主动下载(这个可以不设置)
ctx.Header("Content-Type", "application/octet-stream")
// 可以设置下载的文件名称(这个必须设置,不然下载的文件 没有后缀)
ctx.Header("Content-Disposition", "attachment; filename="+"文件名.后缀")
// 响应下载路径
ctx.File(filePath)
TIP
ctx.Header("Content-Disposition", "attachment; filename="+"文件名.后缀")
,这个请求头一定要设置,不然下载的文件没有后缀名,如下代码可实现返回前端文件,
ctx.Header("Content-Type", "application/octet-stream")
可以不设置
Go
ctx.Header(
"Content-Disposition",
fmt.Sprintf("attachment;filename=%s", filename)
) // 这个必须要有
filePath := path.Join("uploads", filename)
ctx.File(filePath)
Go
package main
import (
"fmt"
"net/http"
"path"
"github.com/gin-gonic/gin"
)
func fileDownload(ctx *gin.Context) {
filename := ctx.Query("filename")
if len(filename) == 0 {
ctx.JSON(http.StatusOK, gin.H{
"code": -1,
"msg": "参数错误",
"data": nil,
})
return
}
// ctx.Header("Content-Type", "application/octet-stream") // 这个可以没有
// 可以设置下载的文件名称
ctx.Header("Content-Disposition", fmt.Sprintf("attachment;filename=%s", filename)) // 这个必须要有
filePath := path.Join("uploads", filename)
ctx.File(filePath)
}
func main() {
router := gin.Default()
router.GET("/file_download", fileDownload)
router.Run(":9100")
}