【问题标题】:How to get file posted from JSON in go gin?如何在 go gin 中获取从 JSON 发布的文件?
【发布时间】:2022-04-14 12:37:28
【问题描述】:

我想保存 JSON 发布的图像文件。

这是帖子的结构:

type Article struct {
    Title   string `json:"title"`
    Body string `json:"body"`
    File    []byte `json:"file"`
}

处理程序是:

   func PostHandler(c *gin.Context) {
        var err error
        var json Article
        err = c.BindJSON(&json)
        if err != nil {
            log.Panic(err)
        }

    //handle photo upload
        var filename string
        file, header, err := json.File  //assignment count mismatch: 3 = 1

        if err != nil {
            fmt.Println(err)
            filename = ""

        } else {
            data, err := ioutil.ReadAll(file)
            if err != nil {
                fmt.Println(err)
                return
            }

            filename = path.Join("media", +shared.RandString(5)+path.Ext(header.Filename))

            err = ioutil.WriteFile(filename, data, 0777)
            if err != nil {
                io.WriteString(w, err.Error())
                return
            }

        }
...

但我明白了

分配计数不匹配:3 = 1

我从一个工作正常的多部分表单处理程序复制了文件处理部分,但显然,

file, header, err := r.FormFile("uploadfile")

不能翻译成JSON处理。

我查看了 gin 文档,但找不到涉及 json 文件处理的示例。 那么我该如何解决呢?

【问题讨论】:

  • 您可以使用JSON上传文件,如果您将文件base64编码为字符串,然后base64将字符串解码为服务器上的缓冲区(字节数组)。但是,我建议您改用“multipart/form-data”方法,因为它更适合处理图像文件。有关详细信息,请参阅下面的答案。

标签: json go go-gin


【解决方案1】:

使用 Gin 获取上传的文件

我认为您的问题是“使用 Gin,我如何获取上传的文件?”。大多数开发人员不会上传带有 JSON 编码的文件,这可以做到,但需要将文件包含为 base64 字符串(并且会增加文件大小约 33%)。

常见(且更有效)的做法是使用“multipart/form-data”编码上传文件。其他人提供的代码file, header, err := c.Request.FormFile("file") 可以工作,但它劫持了 Gin 扩展的下划线“net/http”包。

我的建议是使用ShouldBind,但你也可以使用Gin包提供的FormFileMultipartForm方法。

以下示例。 Gin 页面https://github.com/gin-gonic/gin#model-binding-and-validationhttps://github.com/gin-gonic/gin#upload-files 也提供了类似(但不太详细)的解释。


上传一个文件


客户

HTML

<form action="/upload" method="POST">
  <input type="file" name="file">
  <input type="submit">
</form>

卷曲

curl -X POST http://localhost:8080/upload \
  -F "file=../path-to-file/file.zip" \
  -H "Content-Type: multipart/form-data"

服务器

package main

import (
    "github.com/gin-gonic/gin"
    "log"
    "net/http"
    "io/ioutil"
)

type Form struct {
    File *multipart.FileHeader `form:"file" binding:"required"`
}

func main() {
    router := gin.Default()

    // Set a lower memory limit for multipart forms (default is 32 MiB)
    // router.MaxMultipartMemory = 8 << 20  // 8 MiB

    router.POST("/upload", func(c *gin.Context) {

        // Using `ShouldBind`
        // --------------------
        var form Form
        _ := c.ShouldBind(&form)

        // Get raw file bytes - no reader method
        // openedFile, _ := form.File.Open()
        // file, _ := ioutil.ReadAll(openedFile)

        // Upload to disk
        // `form.File` has io.reader method
        // c.SaveUploadedFile(form.File, path)
        // --------------------

        // Using `FormFile`
        // --------------------
        // formFile, _ := c.FormFile("file")

        // Get raw file bytes - no reader method
        // openedFile, _ := formFile.Open()
        // file, _ := ioutil.ReadAll(openedFile)

        // Upload to disk
        // `formFile` has io.reader method
        // c.SaveUploadedFile(formFile, path)
        // --------------------
        
        c.String(http.StatusOK, "Files uploaded")
    })

    // Listen and serve on 0.0.0.0:8080
    router.Run(":8080")
}

上传多个文件


客户

HTML

<form action="/upload" method="POST" multiple="multiple">
  <input type="file" name="files">
  <input type="submit">
</form>

卷曲

curl -X POST http://localhost:8080/upload \
  -F "files=../path-to-file/file1.zip" \
  -F "files=../path-to-file/file2.zip" \
  -H "Content-Type: multipart/form-data"

服务器

package main

import (
    "github.com/gin-gonic/gin"
    "log"
    "net/http"
    "io/ioutil"
)

type Form struct {
    Files []*multipart.FileHeader `form:"files" binding:"required"`
}

func main() {
    router := gin.Default()

    // Set a lower memory limit for multipart forms (default is 32 MiB)
    // router.MaxMultipartMemory = 8 << 20  // 8 MiB

    router.POST("/upload", func(c *gin.Context) {

        // Using `ShouldBind`
        // --------------------
        var form Form
        _ := c.ShouldBind(&form)

        // for _, formFile := range form.Files {

          // Get raw file bytes - no reader method
          // openedFile, _ := formFile.Open()
          // file, _ := ioutil.ReadAll(openedFile)

          // Upload to disk
          // `formFile` has io.reader method
          // c.SaveUploadedFile(formFile, path)

        // }
        // --------------------

        // Using `MultipartForm`
        // --------------------
        // form, _ := c.MultipartForm()
        // formFiles, _ := form["files[]"]

        // for _, formFile := range formFiles {

          // Get raw file bytes - no reader method
          // openedFile, _ := formFile.Open()
          // file, _ := ioutil.ReadAll(openedFile)

          // Upload to disk
          // `formFile` has io.reader method
          // c.SaveUploadedFile(formFile, path)

        // }
        // --------------------
        
        c.String(http.StatusOK, "Files uploaded")
    })

    // Listen and serve on 0.0.0.0:8080
    router.Run(":8080")
}

【讨论】:

    【解决方案2】:

    在你的代码中你说var json Article 类型文章被定义为

    type Article struct {
        Title   string `json:"title"`
        Body string `json:"body"`
        File    []byte `json:"file"`
    }
    

    文件的类型为[]byte。类型 byte 不会返回除了它所持有的任何东西

    您的Article.Filer.FormFile 不同,其中FormFile 是返回3 个项目的方法

    所以file, header, err := json.File 不是file, header, err := r.FormFile("foo")

    见godocs的实现和方法描述-> here

    【讨论】:

    • 您可能应该阅读 gin 文档。这里是文档中向您展示 gin 的 FormFile godoc.org/github.com/gin-gonic/gin#Context.FormFile 的方法的地方,它处理上传 godoc.org/github.com/gin-gonic/gin#Context.MultipartForm.. 就在那里花点时间了解文档。
    • 仍然无法弄清楚如何做到这一点。我需要一个例子。
    • 我试过file, header, err := c.FormFile(json.File),但我得到了这个错误:c.FormFile undefined (type *gin.Context has no field or method FormFile)
    • 阅读文档。func (c *Context) FormFile(name string) (*multipart.FileHeader, error) FormFile 函数要求您将字符串传递给它。您正在向它传递一个字节片。 string(json.File) 另外,如果 *gin.Context 没有 FormFile 字段或方法,这是一个单独的问题,因为根据文档,这包含在上下文中。
    • 我也试过file, header, err := c.FormFile("file")并得到: c.FormFile undefined (type *gin.Context has no field or method FormFile)
    猜你喜欢
    • 1970-01-01
    • 2019-11-08
    • 2017-07-04
    • 1970-01-01
    • 2021-02-28
    • 2018-04-21
    • 2016-10-15
    • 2022-06-30
    • 1970-01-01
    相关资源
    最近更新 更多