文件的上传与下载
单文件上传
package main
import (
"fmt"
"github.com/gin-gonic/gin"
)
func main() {
router := gin.Default()
router.POST("/upload", func(c *gin.Context) {
//只有form-data的形式才能上传文件,json也可以传文件,但是要以字节流的形式,和这个不太一样
_file, _ := c.FormFile("file")
fmt.Println(_file.Filename)
//file.Size可限制用户上传文件的大小,以字节为单位
fmt.Println(_file.Size % 1024)
c.JSON(200, gin.H{"msg": "文件上传成功"})
c.SaveUploadedFile(_file, "./upload/temp.png")
})
router.Run(":80")
}
——————————————————————————————————————————————
读取上传的文件(单文件)
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"io"
)
func main() {
router := gin.Default()
router.POST("/upload", func(c *gin.Context) {
file, _ := c.FormFile("file")
//实际工作中要处理error,而不是用_替代
readerFile, _ := file.Open()
data, _ := io.ReadAll(readerFile)
fmt.Println(string(data))
c.JSON(200, gin.H{"msg": "上传成功"})
})
router.Run(":80")
}
这里我们可以用file.open()的功能读取用户上传的文件内容,然后去做一些事情,例如查看用户上传内容是否非法等。
——————————————————————————————————————————————
服务端保存文件的几种方式
SaveUploadedFile
c.SaveUploadedFile(file, address)
Create + Copy
file.Open的第一个返回值就是我们讲文件对象中的那个文件(只读的),我们可以使用这个去直接读取文件内容
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"io"
"os"
)
func main() {
router := gin.Default()
router.POST("/upload", func(c *gin.Context) {
file, _ := c.FormFile("file")
//实际工作中要处理error,而不是用_替代
readerFile, _ := file.Open()
writerFile, _ := os.Create("./upload/temp1.png")
defer writerFile.Close()
//意思就是创建一个temp1.png,将readerFile(用户上传的temp.png)给copy进temp1.png
n, _ := io.Copy(writerFile, readerFile)
fmt.Println(n)
c.JSON(200, gin.H{"msg": "上传成功"})
})
router.Run(":80")
}
——————————————————————————————————————————————
多文件上传
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"io"
"os"
)
func main() {
router := gin.Default()
//多文件上传
router.POST("/uploads", func(c *gin.Context) {
form, _ := c.MultipartForm()
//File是一个map
//upload[]是apipost里form-data里参数名填写的名字
files, _ := form.File["upload[]"]
for _, file := range files {
c.SaveUploadedFile(file, "./upload/"+file.Filename)
}
c.JSON(200, gin.H{"mas": fmt.Sprintf("成功上传 %d 个文件", len(files))})
})
router.Run(":80")
}
——————————————————————————————————————————————
文件下载(单前端)
直接这样c.File("upload/temp1.png")
,但会发现这样只能看到图片,不能下载(部分文件可以以这样的方式直接下载,但占少数)。
所以我们需要使浏览器唤起下载行为:
c.Header("Content-Type", "application/octet-stream") //唤起浏览器下载文件流
c.Header("Content-Disposition", "attachment; filename="+"文件名.后缀") // 用来指定下载下来的文件名。如果用了上面的唤起浏览器下载文件流,就一定要用这一个。(如果不加这个,老版本是我看教程是可以下载的,但是新版本我自己试了试貌似不行了)。这里的文件名是自己设的,设的什么下下来的文件名就是什么。
c.Header("Content-Transfer-Encoding", "binary") // 表示传输过程中的编码形式,乱码问题可能就是因为它。(这个有时候有用,有时候又用不到,看具体情况,主要用于文件下载时重叠的情况)
c.File("upload/temp1.png")
小技巧:如果浏览器缓存了导致每次下载的东西都一样,但要是不一样的,就可以在网页f12,新定义一个时间戳new Date().getTime()
,然后再url后面?+获得的时间戳
即可。(这个方法好啊!)
——————————————————————————————————————————————
前后端模式下的文件下载
前后端模式下,后端就只需要响应头,去响应一个文件数据。
文件名和其他信息就写在请求头中。
如果出问题了,能直接将错误信息传递过来。因为c.File()
是没有返回值的,所以错误了也不知道,只能自己写一个。
后端:
//响应头中设置文件的名字以及文件的响应信息(例如错误信息或者成功信息等)
c.Header("filename","xxx.后缀")
c.Header("msg":"文件下载成功")
c.File("upload/temp1.png")
前端:(这里前端就要写的复杂一些了,以下是前端下载的通用写法)
async downloadFile(row) {
this.$http({
method: 'post',
url: 'file/upload',
data:postData,
responseType: "blob" //responseType的blob是指XMLHttpRequest对象的一个属性,用于指定响应的数据类型为Blob对象(二进制大对象)。Blob对象是一种特殊的文件对象,可以存储二进制数据,如图像、音频、视频等。
}).then(res => {
const _res = res.data //响应的data是后端c.JSON的data,code就是我们c.JSON里面的code(状态码)
let blob = new Blob([_res], {
type: 'application/png'
});
let downloadElement = document.createElement("a"); //创建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对象
})}
//响应的时候判断响应
//要不然就是上面这种
//响应有时候不用data,就让它直接拿原信息就好。
//vue3里写法不一样,如果要用vue3看看理解理解就好
评论(0)