Golang中如何处理上传的文件
Golang中如何处理上传的文件 大家好,我有一段代码,它从请求中获取一个多部分文件,并将其发送给负责将其作为电子邮件附件发送的包。我遇到的问题在于,当我尝试直接从请求中获取文件并发送时,文件内容为空;而如果在发送之前运行一些其他代码,通过电子邮件发送的文件就正常了。我实在不明白为什么会发生这种情况,任何有助于理解这里到底发生了什么问题的帮助都将不胜感激 🙏 🙏
var files []*os.File
uploadedFilesData := req.MultipartForm.File["attachments"]
for _, v := range uploadedFilesData {
mFile, err := v.Open()
if err != nil {
helpers.ClientError(w, err, http.StatusUnprocessableEntity)
return
}
fileData, err := ioutil.ReadAll(mFile)
mFile.Close()
if err != nil {
helpers.ServerError(w, err, ec.appLogger, "Failed to read the multipart file data")
return
}
almostUniqueFileName := strconv.Itoa(int(time.Now().Unix())) + v.Filename
fileName := "/tmp/kb_messaging/" + almostUniqueFileName
newFile, err := os.Create(fileName)
if err != nil {
helpers.ServerError(w, err, ec.appLogger, "Failed to create tmp file")
return
}
_, err = newFile.Write(fileData)
if err != nil {
helpers.ServerError(w, err, ec.appLogger, "Failed to write multipart file data to tmp file")
return
}
files = append(files, newFile)
}
status, messageID, mErr := message.SendEmailWithFileAttachments(files, emailModel.Receiver)
虽然这段代码能运行,但附件只是空文件。但是,如果我将存储文件的逻辑与发送前重新读取它们的逻辑分开,文件就不再是空的了。
uploadedFilesData := req.MultipartForm.File["attachments"]
for _, v := range uploadedFilesData {
mFile, err := v.Open()
if err != nil {
helpers.ClientError(w, err, http.StatusUnprocessableEntity)
return
}
fileData, err := ioutil.ReadAll(mFile)
mFile.Close()
if err != nil {
helpers.ServerError(w, err, ec.appLogger, "Failed to read the multipart file data")
return
}
almostUniqueFileName := strconv.Itoa(int(time.Now().Unix())) + v.Filename
fileName := "/tmp/kb_messaging/" + almostUniqueFileName
newFile, err := os.Create(fileName)
if err != nil {
helpers.ServerError(w, err, ec.appLogger, "Failed to create tmp file")
return
}
_, err = newFile.Write(fileData)
if err != nil {
helpers.ServerError(w, err, ec.appLogger, "Failed to write multipart file data to tmp file")
return
}
}
var files []*os.File
tmpDir, err := ioutil.ReadDir("/tmp/kb_messaging/")
if err != nil {
helpers.ServerError(w, result.Error, ec.appLogger, "Failed to open tmp dir")
return
}
for _, v := range tmpDir {
newFile, err := os.Open("/tmp/kb_messaging/" + v.Name())
if err != nil {
helpers.ServerError(w, result.Error, ec.appLogger, "Failed to read file from tmp dir")
return
}
defer newFile.Close()
files = append(files, newFile)
}
status, messageID, mErr := message.SendEmailWithFileAttachments(files, emailModel.Receiver)
这真的很令人困惑,虽然第二种方法有效,但我实在不明白为什么第一种方法没有按预期工作! 附注:我使用的是自己开发的这个包,用于帮助通过 Mailgun 发送电子邮件:https://pkg.go.dev/github.com/nizigama/gomailer
更多关于Golang中如何处理上传的文件的实战教程也可以访问 https://www.itying.com/category-94-b0.html
关闭它并没有太大改变,问题在于当负责将其作为邮件附件发送的包尝试读取文件时,通过文件切片传递给它的文件,在使用 ioutil.ReadAll 读取时会返回一个“无效参数”错误。
更多关于Golang中如何处理上传的文件的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
感谢您的回复,但问题是,当您尝试使用 ioutil.readall 读取新创建文件的数据时,无论是否延迟关闭文件,都会得到一个空切片,结果是一样的,一个空切片。https://play.golang.org/p/Vai_ly0nbnM
如果你使用的是 readAll,那么在读取之前需要先打开文件;而如果你使用 readFile,则无需打开。
大家好,我是Go语言和编程的新手,所以我也很想了解更多关于这方面的信息。
我在想,添加一个 newFile.Close() 会不会改变行为?我无法从技术角度准确解释,但这可能与文件IO操作被认为尚未完成有关,也许?
newFile, err := os.Create(fileName)
if err != nil {
helpers.ServerError(w, err, ec.appLogger, "Failed to create tmp file")
return
}
_, err = newFile.Write(fileData)
if err != nil {
helpers.ServerError(w, err, ec.appLogger, "Failed to write multipart file data to tmp file")
return
}
defer newFile.Close()
files = append(files, newFile)
非常感谢 @Gowtham_Girithar,我想我现在明白发生了什么。
newFile, err := os.Create(fileName)
if err != nil {
fmt.Println("Create error", err)
return
}
_, err = newFile.Write(fileData)
这段代码将数据写入文件,但不会将其保留在程序的内存中(我指的是变量中)。这就是为什么当我尝试使用 ioutil.ReadAll 读取文件数据时,会得到一个空切片。但是,当我使用 ioutil.ReadFile 并传入文件名时,我就能获取到数据,因为 ioutil.ReadFile 会从系统中打开文件,然后使用 ReadAll 来获取字节,就像这样:
bx, err := ioutil.ReadFile(newFile.Name())
if err != nil {
fmt.Println("Error reading file data", err)
http.Error(writer, err.Error(), http.StatusInternalServerError)
return
}
fmt.Println("byte data")
fmt.Printf("byte data:%v\nbytes count %v\n", bx, len(bx))
再次感谢 @Gowtham_Girithar 的帮助,我真的很感激 
问题出在文件指针的位置。在第一种方法中,你创建文件并写入数据后,文件指针位于文件末尾。当你将这个 *os.File 直接传递给邮件发送函数时,读取会从当前位置(即文件末尾)开始,导致读取到空内容。
以下是修复后的代码:
var files []*os.File
uploadedFilesData := req.MultipartForm.File["attachments"]
for _, v := range uploadedFilesData {
mFile, err := v.Open()
if err != nil {
helpers.ClientError(w, err, http.StatusUnprocessableEntity)
return
}
fileData, err := io.ReadAll(mFile)
mFile.Close()
if err != nil {
helpers.ServerError(w, err, ec.appLogger, "Failed to read the multipart file data")
return
}
almostUniqueFileName := strconv.Itoa(int(time.Now().Unix())) + v.Filename
fileName := "/tmp/kb_messaging/" + almostUniqueFileName
newFile, err := os.Create(fileName)
if err != nil {
helpers.ServerError(w, err, ec.appLogger, "Failed to create tmp file")
return
}
_, err = newFile.Write(fileData)
if err != nil {
helpers.ServerError(w, err, ec.appLogger, "Failed to write multipart file data to tmp file")
return
}
// 关键修复:将文件指针重置到文件开头
_, err = newFile.Seek(0, 0)
if err != nil {
helpers.ServerError(w, err, ec.appLogger, "Failed to seek file")
return
}
files = append(files, newFile)
}
status, messageID, mErr := message.SendEmailWithFileAttachments(files, emailModel.Receiver)
或者,你也可以在写入后关闭文件,然后在需要时重新打开:
var files []*os.File
uploadedFilesData := req.MultipartForm.File["attachments"]
for _, v := range uploadedFilesData {
mFile, err := v.Open()
if err != nil {
helpers.ClientError(w, err, http.StatusUnprocessableEntity)
return
}
fileData, err := io.ReadAll(mFile)
mFile.Close()
if err != nil {
helpers.ServerError(w, err, ec.appLogger, "Failed to read the multipart file data")
return
}
almostUniqueFileName := strconv.Itoa(int(time.Now().Unix())) + v.Filename
fileName := "/tmp/kb_messaging/" + almostUniqueFileName
// 写入文件并立即关闭
err = os.WriteFile(fileName, fileData, 0644)
if err != nil {
helpers.ServerError(w, err, ec.appLogger, "Failed to write tmp file")
return
}
// 重新打开文件用于邮件发送
newFile, err := os.Open(fileName)
if err != nil {
helpers.ServerError(w, err, ec.appLogger, "Failed to open tmp file")
return
}
defer newFile.Close()
files = append(files, newFile)
}
status, messageID, mErr := message.SendEmailWithFileAttachments(files, emailModel.Receiver)
第二种方法有效是因为 os.Open() 打开文件时,文件指针默认在文件开头,而 os.Create() 和写入操作后,文件指针在文件末尾。


