0

I have an application where users can bulk download images. The client makes a post request with some file ids, the server fetches the filenames and then loops over the filenames, fetching them and writing to zip.

The zipping itself is fine (verified by checking an actual file with os.Create and inspecting it), but I am also trying to avoid saving the resulting zip file and instead directly streaming it to the browser as a response by using io.Copy.

func (c *controller) GetZipped(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
...

  files, err := services.FetchImageFileNames(imageIds, id, imgC.db)

  if err != nil {
      http.Error(w, http.StatusText(500), 500)
      return
  }

  zipName := time.Now().Format("2006-01-02") + ".zip"

  w.Header().Add("Content-Disposition", "attachment; filename=\""+zipName+"\"")
  w.Header().Add("Content-Type", "application/zip")

  if err != nil {
      http.Error(w, http.StatusText(500), 500)
      return
  }

  zipWriter := zip.NewWriter(w)
  zipWriter.Flush()

  // Loop over files, fetch the image and add to zip
  for _, file := range files {
      resp, err := http.Get(c.rcUrl + file)

      if err != nil {
          continue
      }

      defer resp.Body.Close()

      h := &zip.FileHeader{Name: file, Method: zip.Deflate}

      f, err := zipWriter.CreateHeader(h)

      if err != nil {
          continue
      }

      io.Copy(f, resp.Body)
      resp.Body.Close()
  }

  zipWriter.Close()
}

In Chrome Dev Tools, I see that the response for this request is receiving data as it streams down, but the files is not downloaded and saved anywhere.

I'd like to avoid saving the zip file and just directly stream the file down, so we can avoid having to do any clean up.

Is this an issue with the browser not recognising the Content Disposition? I've also tried various Content-Type (octet stream etc.) but still no luck.

I was using this as a guide: https://engineroom.teamwork.com/how-to-securely-provide-a-zip-download-of-a-s3-file-bundle/

Any help is much appreciated!

Andrew Chung
  • 41
  • 2
  • 4
  • You say "In Chrome Dev Tools, I see that the response for this request is receiving data as it streams down, but the files is not downloaded and saved anywhere." how are you initiating the request? What happens when it completes in Chrome? Have you tried other browsers? – Adrian Jun 16 '17 at 13:42
  • The request is initiated via a button (no form) on the client which makes a post request to the API server. Within that same request the files are zipped up and responded. Once it completes in Chrome, I get a 200 response and that's it. – Andrew Chung Jun 16 '17 at 14:18
  • I have tried in Safari and gotten basically the same result. The size of the network request and transferred indicate that data is streamed down but there's just no prompt to save the file... – Andrew Chung Jun 16 '17 at 14:22
  • 2
    Are you doing this via AJAX request in JS? If so the file won't be saved and you won't be prompted to save it. The expectation with an AJAX request is that the body will be handled by the script. To download a file, the browser actually has to navigate to it in some way (link, form submit, iframe, etc.) – Adrian Jun 16 '17 at 14:32
  • If you want, you can initiate the download via javascript, refer this [SO post](https://stackoverflow.com/a/7660817) As @Adrian mentioned, you cannot download file via AJAX, browser won't give a prompt. You handler code snippet looks good to me. – jeevatkm Jun 16 '17 at 20:42
  • @Adrian - Thank you. I eventually split the handler into 2 - the first one generates a download token, and the client uses that token to make a request to the second handler (by clicking an a link), which downloads the files based off of the token. – Andrew Chung Jun 17 '17 at 02:07
  • @jeevatkm Thank you as well – Andrew Chung Jun 17 '17 at 02:07

0 Answers0