5. 文件上传和下载
5.1 文件上传
将上传到文件保存到服务器:
- 单个文件
- 直接用
saveUploadedFile()函数,接收两个参数,第一个参数是要保存的文件的指针 第二个是保存路径,(路径不仅仅是保存的目录,而且指明保存后的文件名,如:./uploads/t1.png·) - 用创建文件的方式,将读到的内容保存到新文件中
- 多个文件
将上次的文件从切片中取出,展开然后保存。
获取到上传的所有文件名:uploadFiles是post表单中接收上传的多个文件的自定义的切片名,那么用map的方法获取即可 files := form.File["uploadFiles"]
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"io"
"mime/multipart"
"net/http"
"os"
)
func _saveF1(c *gin.Context, file *multipart.FileHeader) {
// 保存文件
err := c.SaveUploadedFile(file, "./uploads/t1.png")
if err != nil {
panic(err)
}
}
func _readFile(c *gin.Context) (file *multipart.FileHeader) {
file, err := c.FormFile("file") // 获取表单中
if err != nil {
panic(err)
}
fmt.Println("文件名为:", file.Filename)
fmt.Println("文件大小为:", file.Size/1024) //单位是字节
// 打开文件
fileReader, err := file.Open()
if err != nil {
panic(err)
}
// 获取到文件内容并打印出来
data, err := io.ReadAll(fileReader)
if err != nil {
panic(err)
}
fmt.Println("文件内容:", string(data))
return file // 返回读取到的文件
}
func _saveF2(file *multipart.FileHeader) {
readerFile, err := file.Open()
if err != nil {
panic(err)
}
writerFile, err := os.Create("./uploads/pwd.txt")
defer writerFile.Close() // 关闭文件
// 第二个参数是源文件内容 第一个是目的文件
// 返回两个参数 一个是拷贝到文件大小(单位:字节),另一个是error
n, err := io.Copy(writerFile, readerFile)
if err != nil {
panic(err)
}
fmt.Println("拷贝的文件大小为:", n)
}
func main() {
router := gin.Default()
// 单个文件
router.POST("/upload", func(c *gin.Context) {
// 读取文件
file := _readFile(c)
c.JSON(http.StatusOK, gin.H{"msg": "上传成功"})
// 保存文件的各种方法
// _saveF1(c,file)
_saveF2(file)
})
// 多个文件
router.POST("/uploads", func(c *gin.Context) {
form, err := c.MultipartForm()
if err != nil {
panic(err)
}
// 获取到上传的所有文件名 uploadFiles是post表单中接收上传的多个文件的自定义的切片名
files := form.File["uploadFiles"]
for _, f := range files {
err := c.SaveUploadedFile(f, "./uploads/"+f.Filename)
if err != nil {
panic(err)
}
fmt.Println("已保存" + f.Filename)
}
c.JSON(http.StatusOK, gin.H{"msg": fmt.Sprintf("成功上传 %d 个文件", len(files))})
})
err := router.Run(":80") // 不写就默认在 8080 端口
if err != nil {
panic(err)
}
}
5.2 文件下载
通过客户端向服务端发出请求,服务端响应下载的请求。
先设置响应头的字段,然后响应给客户端:需要设置响应头的 "Content-Type" 字段,指示发送的数据是一个二进制流(octet-stream)。这是告诉浏览器接收到的是一个二进制文件,而不是普通的文本 。"Content-Disposition" 设置为attachment,指定以附件形式下载文件。
router.GET("/download", func(c *gin.Context) {
// 先设置响应头
// 设置响应头的 "Content-Type" 字段
c.Header("Content-Type", "application/octet-stream")
// 指定以附件形式下载文件。它还通过 "filename" 参数指定了下载文件的名称为 "download.png"
c.Header("Content-Disposition", "attachment; filename="+"download.png")
// 表示传输过程中的编码形式,乱码问题可能就是因为它。在文件下载过程中,这通常被设置为 "binary",表示二进制传输,避免数据在传输过程中被修改。
c.Header("Content-Transfer-Encoding", "binary")
// 再响应给客户端
// 向客户端发送指定文件的内容作为响应 此处,将名为 "t1.png" 的文件发送给客户端
c.File("./uploads/t1.png")
})