Handling uploaded file

Hello everyone, so am having this code that gets a multipart file from the request and sends it to a package in charge of sending it as an email attachment, the problem am facing is when i try to send the file directly as i fetch it from the request it just goes empty while if there is some other code running after, the file sending through email goes well. So am really not getting why this is happening, any help understanding what’s really happening here would be very appreciated :pray:t5: :pray:t5:

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)

Although this code does run, the attachments are just empty files. But if i separate the logic for storing the files and retrieving them before sending again, the files are now not empty.

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)

This is really confusing although it works i don’t really understand why the first approach is not working as expected!
PS: am using this package, i developed myself for helping to send emails using mailgun: https://pkg.go.dev/github.com/nizigama/gomailer

Hi there, I’m new to Go and programming in general so I’m interested to find out more about this as well.

I wonder if adding a newFile.Close() would change the behaviour? I can’t explain this with technical accuracy but it has something to do with the file IO operation not deemed as completed, maybe?

    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)

I’m able to get data from files. Just printing the size to check this.

Closing it doesn’t change much, the problem is when the package in charge of sending it as a mail attachment tries to read the file ,passed to it through the files slice, using ioutil.ReadAll it returns an “Invalid argument” error

Thanks for the reply, but the problem is when you try to read data of that newly created file using ioutil.readall you get an empty slice whether your defer the close of the file or not the result is the same, an empty slice. https://play.golang.org/p/Vai_ly0nbnM

Need to open the file before reading if you are using readAll and if you use readFile, then no need to open.

https://play.golang.org/p/b2zZTHMp9nF

2 Likes

Thank you very much @Gowtham_Girithar i think i understand what happens now

newFile, err := os.Create(fileName)

		if err != nil {
			fmt.Println("Create error", err)
			return
		}

		_, err = newFile.Write(fileData)

This writes data to the file but doesn’t keep it in the program’s memory(i mean variable) that’s why when i try to read the file’s data using ioutil.readall i get an empty slice but when i use ioutil.ReadFile passing the file name i get the data cause ioutil.readfile opens the file from the system and then uses readall to get the bytes like this.

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))

Thanks again @Gowtham_Girithar for your help, i really appreciate :pray:t5: