文件上传
单文件
func main() {
router := gin.Default()
// 为 multipart forms 设置较低的内存限制 (默认是 32 MiB)
// 单位是字节, << 是左移预算符号,等价于 8 * 2^20
// gin对文件上传大小的默认值是32MB
router.MaxMultipartMemory = 8 << 20 // 8 MiB
router.POST("/upload", func(c *gin.Context) {
// 单文件
file, _ := c.FormFile("file")
log.Println(file.Filename)
dst := "./" + file.Filename
// 上传文件至指定的完整文件路径
c.SaveUploadedFile(file, dst)
c.String(http.StatusOK, fmt.Sprintf("'%s' uploaded!", file.Filename))
})
router.Run(":8080")
}
服务端保存文件的几种方式
SaveUploadedFile
c.SaveUploadedFile(file, dst) // 文件对象 文件路径,注意要从项目根路径开始写
Create+Copy
file.Open的第一个返回值就是我们讲文件对象中的那个文件(只读的),我们可以使用这个去直接读取文件内容
file, _ := c.FormFile("file")
log.Println(file.Filename)
// 读取文件中的数据,返回文件对象
fileRead, _ := file.Open()
dst := "./" + file.Filename
// 创建一个文件
out, err := os.Create(dst)
if err != nil {
fmt.Println(err)
}
defer out.Close()
// 拷贝文件对象到out中
io.Copy(out, fileRead)
读取上传的文件
file, _ := c.FormFile("file")
// 读取文件中的数据,返回文件对象
fileRead, _ := file.Open()
data, _ := io.ReadAll(fileRead)
fmt.Println(string(data))
这里的玩法就很多了
例如我们可以基于文件中的内容,判断是否需要保存到服务器中
多文件上传
func main() {
router := gin.Default()
// 为 multipart forms 设置较低的内存限制 (默认是 32 MiB)
router.MaxMultipartMemory = 8 << 20 // 8 MiB
router.POST("/upload", func(c *gin.Context) {
// Multipart form
form, _ := c.MultipartForm()
files := form.File["upload[]"] // 注意这里名字不要对不上了
for _, file := range files {
log.Println(file.Filename)
// 上传文件至指定目录
c.SaveUploadedFile(file, "./"+file.Filename)
}
c.String(http.StatusOK, fmt.Sprintf("%d files uploaded!", len(files)))
})
router.Run(":8080")
}

文件下载
直接响应一个路径下的文件
c.File("uploads/12.png")
有些响应,比如图片,浏览器就会显示这个图片,而不是下载,所以我们需要使浏览器唤起下载行为
c.Header("Content-Type", "application/octet-stream") // 表示是文件流,唤起浏览器下载,一般设置了这个,就要设置文件名
c.Header("Content-Disposition", "attachment; filename="+"牛逼.png") // 用来指定下载下来的文件名
c.Header("Content-Transfer-Encoding", "binary") // 表示传输过程中的编码形式,乱码问题可能就是因为它
c.File("uploads/12.png")
注意,文件下载浏览器可能会有缓存,这个要注意一下
解决办法就是加查询参数
前后端模式下的文件下载
如果是前后端模式下,后端就只需要响应一个文件数据
文件名和其他信息就写在请求头中
c.Header("fileName", "xxx.png")
c.Header("msg", "文件下载成功")
c.File("uploads/12.png")
前端写法
async downloadFile(row) {
this.$http({
method: 'post',
url: 'file/upload',
data:postData,
responseType: "blob"
}).then(res => {
const _res = res.data
let blob = new Blob([_res], {
type: 'application/png'
});
let downloadElement = document.createElement("a");
let href = window.URL.createObjectURL(blob); //创建下载的链接
downloadElement.href = href;
downloadElement.download = res.headers["fileName"]; //下载后文件名
document.body.appendChild(downloadElement);
downloadElement.click(); //点击下载
document.body.removeChild(downloadElement); //下载完成移除元素
window.URL.revokeObjectURL(href); //释放掉blob对象
})}
前后端文件下载,中文乱码问题
后端
func Download(c *gin.Context) {
filename := url.QueryEscape("国家机密.txt")
// 可唤起浏览器下载
c.Header("Content-Disposition", "attachment; filename*=utf-8''"+filename) //
c.Header("fileName", filename)
c.File("uploads/国家机密.txt")
}
前端
async download() {
let res = await axios.get("/download", {headers: {responseType: "blob"}})
if (res.status === 200) {
let binaryData = [];
binaryData.push(res.data);
let url = window.URL.createObjectURL(new Blob(binaryData)); //表示一个指定的file对象或Blob对象
let a = document.createElement("a");
document.body.appendChild(a);
// 转码文件的标题
let filename = decodeURI(res.headers.filename)
// 调起文件下载
a.href = url;
a.download = filename; //命名下载名称
a.click(); //点击触发下载
window.URL.revokeObjectURL(url);
}
}