英文:
How to get file posted from JSON in go gin?
问题
我想保存由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)
}
// 处理照片上传
var filename string
file := json.File
if file == nil {
fmt.Println("文件为空")
filename = ""
} else {
data := file
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
}
}
...
}
但是我得到了
assignment count mismatch: 3 = 1
我从一个工作的多部分表单处理程序中复制了文件处理部分,它工作得很好,但显然
file, header, err := r.FormFile("uploadfile")
不能转换为JSON处理。
我查看了gin文档,但找不到涉及JSON文件处理的示例。那么我该如何解决这个问题?
英文:
I want to save image file posted by JSON.
Here is the struct of the post:
type Article struct {
Title string `json:"title"`
Body string `json:"body"`
File []byte `json:"file"`
}
And the handler is :
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
}
}
...
But I get
> assignment count mismatch: 3 = 1
I copied the file handling part from a working multipart form handler which worked fine but apparently,
file, header, err := r.FormFile("uploadfile")
can not be translated into JSON handling.
I have looked at gin docs but could not find examples involving json file handling.
So how can I fix this?
答案1
得分: 8
使用Gin获取上传的文件
我认为你的问题是“使用Gin,如何获取上传的文件?”。大多数开发者不会使用JSON编码来上传文件,虽然可以这样做,但需要将文件包含为base64字符串(并且会增加文件大小约33%)。
常见(且更高效)的做法是使用“multipart/form-data”编码来上传文件。其他人提供的代码file, header, err := c.Request.FormFile("file")
可以工作,但这会劫持Gin扩展的底层“net/http”包。
我的建议是使用ShouldBind
,但你也可以使用Gin包提供的FormFile
或MultipartForm
方法。
以下是示例。Gin页面上也提供了类似(但不太详细)的解释,链接为https://github.com/gin-gonic/gin#model-binding-and-validation和https://github.com/gin-gonic/gin#upload-files。
上传单个文件
客户端
HTML
<form action="/upload" method="POST">
<input type="file" name="file">
<input type="submit">
</form>
Curl
curl -X POST http://localhost:8080/upload \
-F "file=../path-to-file/file.zip" \
-H "Content-Type: multipart/form-data"
服务器端
Go
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()
// 设置较低的内存限制以处理多部分表单(默认为32 MiB)
// router.MaxMultipartMemory = 8 << 20 // 8 MiB
router.POST("/upload", func(c *gin.Context) {
// 使用`ShouldBind`
// --------------------
var form Form
_ := c.ShouldBind(&form)
// 获取原始文件字节 - 没有读取器方法
// openedFile, _ := form.File.Open()
// file, _ := ioutil.ReadAll(openedFile)
// 上传到磁盘
// `form.File`有io.reader方法
// c.SaveUploadedFile(form.File, path)
// --------------------
// 使用`FormFile`
// --------------------
// formFile, _ := c.FormFile("file")
// 获取原始文件字节 - 没有读取器方法
// openedFile, _ := formFile.Open()
// file, _ := ioutil.ReadAll(openedFile)
// 上传到磁盘
// `formFile`有io.reader方法
// c.SaveUploadedFile(formFile, path)
// --------------------
c.String(http.StatusOK, "Files uploaded")
})
// 监听并在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
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"
服务器端
Go
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()
// 设置较低的内存限制以处理多部分表单(默认为32 MiB)
// router.MaxMultipartMemory = 8 << 20 // 8 MiB
router.POST("/upload", func(c *gin.Context) {
// 使用`ShouldBind`
// --------------------
var form Form
_ := c.ShouldBind(&form)
// for _, formFile := range form.Files {
// 获取原始文件字节 - 没有读取器方法
// openedFile, _ := formFile.Open()
// file, _ := ioutil.ReadAll(openedFile)
// 上传到磁盘
// `formFile`有io.reader方法
// c.SaveUploadedFile(formFile, path)
// }
// --------------------
// 使用`MultipartForm`
// --------------------
// form, _ := c.MultipartForm()
// formFiles, _ := form["files[]"]
// for _, formFile := range formFiles {
// 获取原始文件字节 - 没有读取器方法
// openedFile, _ := formFile.Open()
// file, _ := ioutil.ReadAll(openedFile)
// 上传到磁盘
// `formFile`有io.reader方法
// c.SaveUploadedFile(formFile, path)
// }
// --------------------
c.String(http.StatusOK, "Files uploaded")
})
// 监听并在0.0.0.0:8080上提供服务
router.Run(":8080")
}
英文:
Using Gin to get an uploaded file
I think your question is "Using Gin, how do I get an uploaded file?". Most developers don't upload files with JSON encoding, which could be done but requires the file to be included as a base64 string (and increases the file size about 33%).
The common (and more efficient) practice is to upload the file using the "multipart/form-data" encoding. The code that others provided file, header, err := c.Request.FormFile("file")
works, but that hijacks the underlining "net/http" package that Gin extends.
My recommendation is to use ShouldBind
, but you can also use the FormFile
or MultipartForm
methods provided by the Gin package.
Examples below. Similar (but less detailed) explanations are also offered on the Gin page https://github.com/gin-gonic/gin#model-binding-and-validation and https://github.com/gin-gonic/gin#upload-files.
Upload one file
Clients
HTML
<form action="/upload" method="POST">
<input type="file" name="file">
<input type="submit">
</form>
Curl
curl -X POST http://localhost:8080/upload \
-F "file=../path-to-file/file.zip" \
-H "Content-Type: multipart/form-data"
Server
Go
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")
}
Upload multiple files
Clients
HTML
<form action="/upload" method="POST" multiple="multiple">
<input type="file" name="files">
<input type="submit">
</form>
Curl
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"
Server
Go
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
得分: 2
在你的代码中,你使用了var json Article
,其中类型Article
被定义为:
type Article struct {
Title string `json:"title"`
Body string `json:"body"`
File []byte `json:"file"`
}
而File
的类型是[]byte
。byte
类型除了保存它所包含的内容外,不返回任何其他信息。
你的Article.File
与r.FormFile
不同,其中FormFile
是一个返回3个项目的方法。
所以file, header, err := json.File
不等同于file, header, err := r.FormFile("foo")
。
请参考godocs中的实现和方法描述-> 这里
英文:
In your code you say var json Article
where type article is defined as
type Article struct {
Title string `json:"title"`
Body string `json:"body"`
File []byte `json:"file"`
}
And File is of type []byte
. Type byte doesn't return anything other than what it holds
Your Article.File
is not the same as r.FormFile
where FormFile
is a method that returns 3 items
So file, header, err := json.File
isn't file, header, err := r.FormFile("foo")
See the implementation and method description from godocs -> here
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论