我试图从结构数组中读取文件,但无法读取。 Postman 来了。
下面是我的代码:
type MasterTemplate struct {
ID uuid.UUID `form:"id" json:"id"`
IsVoice bool `form:"is_voice" json:"is_voice"`
Title string `form:"title" json:"title"`
IsSms bool `form:"is_sms" json:"is_sms"`
FilePath string `form:"file_path" json:"file_path"`
Content []struct {
File *multipart.FileHeader `form:"file" json:"file"`
MsgTmplateLangId string `form:"msg_template_lang_id" json:"msg_template_lang_id"`
SMSContent string `form:"sms_content" json:"sms_content"`
MsgContentID string `form:"msg_content_id" json:"msg_content_id"`
} `form:"content" json:"content"`
UserID uuid.UUID `form:"user_id" json:"user_id"`
}
func (tmplController *MessageTemplateController) CreateMsgTemplateController(ctx *gin.Context) {
userID := ctx.Param("user_id")
userUUID, err := uuid.Parse(userID)
if err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{
"error": err.Error(),
})
ctx.Abort()
return
}
if ctx.Request.ContentLength == 0 {
ctx.JSON(http.StatusBadRequest, gin.H{
"error": "Invalid/Empty body request",
})
ctx.Abort()
return
}
err = ctx.Request.ParseMultipartForm(10 * 1024 * 1024)
if err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{
"error": err.Error(),
})
return
}
var msgTemplate *models.MasterTemplate
if err := ctx.ShouldBind(&msgTemplate); err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{
"Gin Error": err.Error(),
})
return
}
config, err := config.LoadConfig("credentials")
if err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{
"rerror": err.Error(),
})
return
}
for _, sms := range msgTemplate.Content {
lanaguageID, err := uuid.Parse(sms.MsgTmplateLangId)
if err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{
"rerror": err.Error(),
})
return
}
templateContent := models.MessageTemplateContenet{
ID: uuid.New(),
LanguageID: lanaguageID,
IsSms: true,
IsVoice: false,
FilePath: "",
Content: sms.SMSContent,
UserID: userUUID,
// MessageTemplateID: data.ID,
IsDeleted: false,
}
_, err = tmplController.contents.CreateMessageTemplateContent(&templateContent, ctx)
if err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{
"rerror": err.Error(),
})
return
}
}
msgTemplate.IsVoice = true
msgTemplate.IsSms = false
msgTemplate.IsSms = false
msgTemplate.IsVoice = true
for x, voice := range msgTemplate.Content {
fmt.Println(voice.MsgTmplateLangId)
fmt.Println(x)
fileHeader, err := ctx.FormFile(fmt.Sprintf("content[%c][file]", x))
if err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{
"error": err.Error(),
})
return
}
file, err := voice.File.Open()
if err != nil {
ctx.JSON(400, gin.H{"error": "Failed to open uploaded file"})
return
}
defer file.Close()
fmt.Println(file)
if !utilities.IsValidFile(fileHeader) {
ctx.JSON(http.StatusBadRequest, gin.H{
"error": "Invalid file format. Only .mp3 and .wav files are allowed.",
})
return
}
if fileHeader.Size > 3*1024*1024 {
ctx.JSON(http.StatusBadRequest, gin.H{
"error": "File size exceeds the limit. Maximum file size is 3MB.",
})
return
}
audioPath := uuid.New().String() + fileHeader.Filename
uploadPath := filepath.Join("./msgtemplate", audioPath)
// Save the uploaded file
if err := ctx.SaveUploadedFile(fileHeader, uploadPath); err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{
"error": err.Error(),
})
return
}
session, err := awsservice.S3BucketSessionHandler()
if err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{
"error": err.Error(),
})
return
}
fileURL, err := awsservice.UploadFiles(session, uploadPath, config.AWS_AUDIO_BUCKET)
if err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{
"error": err.Error(),
})
return
}
lanaguageID, err := uuid.Parse(voice.MsgTmplateLangId)
if err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{
"rerror": err.Error(),
})
return
}
templateContent := models.MessageTemplateContenet{
ID: uuid.New(),
LanguageID: lanaguageID,
IsSms: false,
IsVoice: true,
IsDeleted: false,
FilePath: uploadPath,
Content: *fileURL,
UserID: userUUID,
// MessageTemplateID: data.ID,
}
_, err = tmplController.contents.CreateMessageTemplateContent(&templateContent, ctx)
if err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{
"rerror": err.Error(),
})
return
}
}
}
3条答案
按热度按时间qv7cva1a1#
我看到:
您正在尝试将传入的
multipart
文件绑定到MasterTemplate
结构。具体来说,结构体的以下部分可能存在问题:
当
multipart
文件以这种方式包含在结构体切片中时,ShouldBind
方法可能无法正确绑定这些文件。当您尝试使用以下命令打开文件时:
voice.File
来自以前的结构绑定,可能没有正确绑定,导致在尝试打开文件时出现问题。正如在how to upload multipart file and json in Go with gin gonic?中所看到的,对于简单的情况(当不涉及结构体切片时),表单绑定可以很好地处理文本字段和文件。但是,虽然
ShouldBind
可以处理结构体中的multipart
文件,但当它们位于结构体切片中时,它可能无法正确绑定它们,这是(从您的代码中):在处理更复杂的方案时,将文件数据与其他表单数据分开处理可能更安全。
通用示例:来自Tanveer Shafee Prottoy的“File Upload in Go with multipart/form-data”。
您可以使用
ctx.MultipartForm()
方法和form.File
字段直接从请求中的表单数据中获取文件:然后遍历
files
以根据需要处理每个文件。完整的代码是:
在上面的代码中,
ctx.MultipartForm()
用于从请求中获取多部分表单数据,form.File["content[file]"]
用于访问表单数据中的文件。您必须改进代码(例如,在尝试访问
files
片的元素之前,我甚至没有检查它是否为空)User那么,如何将其他JSON文本附加到文件中?
因为这样我就可以读取文件了,所以我也关心阅读文件结构中的每个文本。
假设您在
multipart/form-data
请求中同时发送文件和相关的JSON数据,那么您需要一种方法来绑定请求的数据部分并单独处理文件。首先,使用
ctx.ShouldBind
将所有文本表单数据绑定到MasterTemplate
结构。然后使用
ctx.MultipartForm()
直接从表单数据中获取文件,并将它们与MasterTemplate.Content
切片中的相应条目相关联。这将从请求中提取文件,并将它们与
msgTemplate.Content
切片中的正确位置相关联。完成此设置后,使用
msgTemplate
的其余代码基本保持不变。您可以访问每个Content
项目中的File
,因为它们现在已经正确关联。v09wglhw2#
x1c 0d1x看起来你正在使用Gin框架来处理一个包含表单字段和文件上传的多部分请求。在你的代码片段中,你定义了一个结构体来绑定传入的请求数据并相应地处理它。我的主要问题是如何包含与文件msg_template_lang_id相关的信息,但无法读取,因为当我打印时,即使我用ctx.Request.MultipartForm读取文件,然后用shouldbind读取其他部分,我也会得到空的。
从提供的代码中,有几件事可能会导致问题:
嵌套结构和多部分文件的绑定问题:在Gin中,用嵌套结构体绑定多部分文件可能很棘手,尤其是当表单具有非平面结构时。
循环中的索引不正确:您使用x作为索引,并尝试使用fmt.Sprintf(“content[%c][file]",x)将其转换为字符。因为x是一个整数,你需要用%d而不是%c将它转换成字符串。
尝试使用FormFile方法直接从请求中获取文件。您可能需要调整表单的字段名称以匹配从客户端发送的内容。确保客户端发送的文件在form-data中具有正确的字段名称。如果您继续面临绑定问题,您可能不得不手动解析多部分表单数据,而不是依赖于Gin的自动绑定。这将涉及遍历多部分表单数据并手动处理每个部分,包括文件。
伪代码:-
确保Postman正确地发送多部分请求,每个文件部分都具有与服务器期望的名称相一致的正确名称。您可能希望在服务器端打印请求,以查看发送的确切内容,并相应地调整代码。确保您的Postman请求设置正确。确保选择“Body”选项卡下的“form-data”,然后根据服务器代码预期的结构输入键和值,包括文件。
mxg2im7a3#
下面是重构后的代码: