Как получить файл, отправленный из JSON в 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)
}
//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 docs, но не смог найти примеров, связанных с обработкой файлов json. Так как я могу это исправить?
1 ответ
Использование Gin для получения загруженного файла
Думаю, ваш вопрос: "Как мне получить загруженный файл с помощью Gin?". Большинство разработчиков не загружают файлы с помощью JSON, что может быть сделано, но требует, чтобы файл был закодирован как строка base64 (и увеличивает размер файла примерно на 33%).
Обычной (и более эффективной) практикой является загрузка файла в кодировке multipart/form-data. Код, предоставленный другими
file, header, err := c.Request.FormFile("file")
работает, но это захватывает подчеркнутый пакет "net / http", который расширяет Gin.
Моя рекомендация - использовать ShouldBind
, но вы также можете использовать FormFile
или же MultipartForm
методы, предоставляемые пакетом Gin.
Примеры ниже. Похожие (но менее подробные) объяснения также предлагаются на странице 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 -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")
}
В своем коде вы говорите var json Article
где тип статьи определяется как
type Article struct {
Title string `json:"title"`
Body string `json:"body"`
File []byte `json:"file"`
}
И файл имеет тип []byte
, Байт типа не возвращает ничего, кроме того, что он содержит
Ваш Article.File
это не то же самое, что r.FormFile
где FormFile
это метод, который возвращает 3 элемента
Так file, header, err := json.File
не file, header, err := r.FormFile("foo")
Смотрите описание реализации и метода от godocs -> здесь