0

I'm loading an archive from Github and want to return it to the browser to download. I successfully get the file and can save it to the local disc (I can open the archive and it's normal - size and content):

headers = {'Authorization': 'token ' + github_token}
zip_response = requests.get(github_url, headers = headers)

output = open("archive.zip", "wb")
output.write(zip_response.content)
output.close()

But when I return it to the client, it's bigger almost twice and archive is corrupted:

zip_content = io.BytesIO(zip_response.content)
return send_file(zip_content, 
                 mimetype='application/zip', 
                 attachment_filename = 'archive.zip')

Javascript:

var url = window.URL.createObjectURL(new Blob([response.data]), {type: 'application/zip'});
var link = document.createElement('a');
link.href = url;
link.setAttribute('download', 'archive.zip');
document.body.appendChild(link);
link.click();

I was trying to use other solutions from this problem: Download File from Bytes in JavaScript

but nothing works.

This just returns 0 bytes:

var bytes = new Uint8Array(response.data)

This throws the exeption "Failed to execute 'atob' on 'Window': The string to be decoded contains characters outside of the Latin1 range.":

var binaryString = window.atob(response.data);

Upd: I found response.data has the normal length but when I create a new Blob object it's 2 times more, I don't know why.

mimic
  • 4,897
  • 7
  • 54
  • 93
  • don't you have problem to open it from local disk? Maybe you save it already corrupted? Maybe create minimal working code so we could run it and see problem. – furas Feb 15 '20 at 19:41
  • @furas The file I save to the disk is totally normal (size and content) and I can open it without any problem. – mimic Feb 15 '20 at 19:50
  • create minimal working code with your problem. I made some minimal code and it downloads file correctly. But you may have something more. – furas Feb 15 '20 at 19:59
  • @furas, I added all the code I have, there is nothing more. – mimic Feb 15 '20 at 20:00
  • create code which we can copy and run - current code is useless. – furas Feb 15 '20 at 20:09
  • BTW: I created code which sends file directly to client without any JavaScript. Do you really need this JavaScript for this? How do you use it? Show code which you use. – furas Feb 15 '20 at 20:10
  • Yes, I need JavaScript. – mimic Feb 15 '20 at 20:16

1 Answers1

0

You're not showing us how you're getting your response.data, but my guess is that you're not setting the responseType property of your xhr object (or you're relying on an old XMLHttpRequest).

Here's a full working example (tested in latest Firefox and Chrome) based on your code:

import io
from flask import Flask, send_file

app = Flask(__name__)

@app.route('/')
def home():
    return  """
<!doctype html>
<html>
<head>
<script>
function getZip() {
       var xhr = new XMLHttpRequest();
       xhr.responseType = 'blob'
       xhr.open('GET', '/sampleZip');
       xhr.onload = function(e) {
           if (this.status == 200) {
              var blob = new Blob([this.response], {type: 'application/zip'});
              var url = window.URL.createObjectURL(blob);
              var link = document.createElement('a');
              link.href = url;
              link.setAttribute('download', 'archive.zip');
              document.body.appendChild(link);
              link.click();
           }
       };
       xhr.send();
       return false;
}
</script>
</head>
<body>
  <button id="btn" onclick="getZip();">Click Me</button>
</body>
</html>"""


@app.route('/sampleZip')
def sampleZip():
    with open("sample.zip", "rb") as f:
        bytes = io.BytesIO(f.read())
    return send_file(bytes, mimetype='application/zip')

If you're stuck relying on an old XMLHttpRequest object then you might want to have a look here (https://www.html5rocks.com/en/tutorials/file/xhr2/) for a hack on getting an old school XHR to treat data as binary and leave it untouched.

clockwatcher
  • 3,193
  • 13
  • 13