Programmer
Programmer

Reputation: 161

Corrupted image file in golang api image download

I am building a simple testing API in golang for uploading and download image files(PNG, JPEG, JPG):

/pic [POST] for uploading the image and save it to a folder; /pic [GET] for downloading the image to the client.

I have successfully built the /pic [POST] and the image is successfully uploaded to the server's file. And I can open the file in the storage folder. (In both windows localhost server and ubuntu server)

However, when I built the /pic [GET] for downloading the picture, I am able to download the file to the client (my computer), but the downloaded file is somehow corrupted since when I try to open it with different image viewer such as gallery or Photoshop, it says "It looks like we don't support this file format". So it seems like the download is not successful.

Postman result: enter image description here

File opening in gallery: enter image description here Any ideas on why this happens and how should I fix it?

The golang code for downloading the pic is as follows(omitting the error handling):

func PicDownload(w http.ResponseWriter, r *http.Request){

   request := make(map[string]string)
   reqBody, _ := ioutil.ReadAll(r.Body)
   err = json.Unmarshal(reqBody, &request)
   // Error handling

   file, err := os.OpenFile("./resources/pic/" + request["filename"], os.O_RDONLY, 0666)
   // Error handling

   buffer := make([]byte, 512)
   _, err = file.Read(buffer)
   // Error handling

   contentType := http.DetectContentType(buffer)

   fileStat, _ := file.Stat()

   // Set header
   w.Header().Set("Content-Disposition", "attachment; filename=" + request["filename"])
   w.Header().Set("Content-Type", contentType)
   w.Header().Set("Content-Length", strconv.FormatInt(fileStat.Size(), 10))

   // Copying the file content to response body
   io.Copy(w, file)

   return
}

Upvotes: 2

Views: 1677

Answers (1)

Jory Geerts
Jory Geerts

Reputation: 1977

When you are reading the first 512 bytes from the file in order to determine the content type, the underlying file stream pointer moves forward by 512 bytes. When you later call io.Copy, reading continues from that position.

There are two ways to correct this.

The first is to call file.Seek(0, io.SeekStart) before the call to io.Copy(). This will place the pointer back to the start of the file. This solution requires the least amount of code, but means reading the same 512 bytes from the file twice which causes some overhead.

The second solution is to create a buffer that contains the entire file using buffer := make([]byte, fileStat.Size() and using that buffer for both the http.DetectContentType() call and to write the output (write it with w.Write(buffer) instead of using io.Copy(). This approach has the possible downside of loading the entire file into memory at once, which isn't ideal for very large files (io.Copy uses 32KB chunks instead of loading the whole file).

Note: As Peter mentioned in a comment, you must ensure users cannot traverse your filesystem by posting ../../ or something as a filename.

Upvotes: 5

Related Questions