gin学习记录 5 - 文件上传和下载 | 青训营;

396 阅读3分钟

5. 文件上传和下载

5.1 文件上传

将上传到文件保存到服务器:

  • 单个文件
  1. 直接用saveUploadedFile()函数,接收两个参数,第一个参数是要保存的文件的指针 第二个是保存路径,(路径不仅仅是保存的目录,而且指明保存后的文件名,如:./uploads/t1.png·)
  2. 用创建文件的方式,将读到的内容保存到新文件中
  • 多个文件

将上次的文件从切片中取出,展开然后保存。

获取到上传的所有文件名: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")  
})