Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JS: Download zip file from a string with Javascript

Tags:

javascript

I want to receive a zip file as a string from an Ajax request and then hold it in memory so it can be downloaded multiple times if necessary so that only the one request is sent.

I tried to download it with this:

zip_string = 'PK etc.'

function download(filename, text) {
  var element = document.createElement('a');
  element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text));
  element.setAttribute('download', filename);

  element.style.display = 'none';
  document.body.appendChild(element);

  element.click();

  document.body.removeChild(element);
}

// Start file download.
download("zip1.zip", zip_string);

It came through as a zip but then there was obviously a problem because it wouldn't open. Can anyone see what I'm doing wrong?

like image 303
Rob Kwasowski Avatar asked Aug 09 '18 04:08

Rob Kwasowski


3 Answers

I solved the problem by encoding the zip file string as base64 on the server before it gets sent through.

with open(file, "rb") as f:
    bytes = f.read()
    encoded = base64.b64encode(bytes)

And then in the JS I just specify that it's base64:

zip_string = 'UEsDBBQAAAAIANQzCU0J56mLPAIAAD4VAAAOAAAA etc.'

function download(filename, data) {
  var element = document.createElement('a');
  element.setAttribute('href', 'data:text/plain;base64,' + data);
  element.setAttribute('download', filename);

  element.style.display = 'none';
  document.body.appendChild(element);

  element.click();

  document.body.removeChild(element);
}

// Start file download.
download("zip1.zip", zip_string);
like image 115
Rob Kwasowski Avatar answered Oct 03 '22 01:10

Rob Kwasowski


The data type of your link's href is plain text (ie as specified by data:text/plain) which basically means, the contents of the link will be treated by the browser as plain text.

A zip archive is a binary format - you will need to do a bit more work to generate a real zip file and then cache it client side, in this way. You may want to look at zip.js as a library to help you with this.

You can however make a simple change to get the download function working - just change "zip1.zip" to "zip1.txt". I've prepared a jsFiddle here if you to see this in action.

Hope that helps!

like image 23
Dacre Denny Avatar answered Oct 03 '22 01:10

Dacre Denny


Using JSZip package: https://stuk.github.io/jszip/

public async downloadFile(filename: string, content: string) {
        const zip = new JSZip();
        await zip.loadAsync(content, {base64: true});
        const blob = await zip.generateAsync({type:"blob"});

        const element = document.createElement("a");
        element.setAttribute("href", window.URL.createObjectURL(blob));
        element.setAttribute("download", filename);
        element.style.display = "none";
        document.body.appendChild(element);
        element.click();
        document.body.removeChild(element);
    }


const fileName = "test.zip";
const content = "UEsDBAoAAAAAAI5ONE9i6ZVwCQAAAAkAAAAIAAAAdGVzdC50eHRUZXN0IGZpbGVQSwECPwAKAAAAAACOTjRPYumVcAkAAAAJAAAACAAkAAAAAAAAACAAAAAAAAAAdGVzdC50eHQKACAAAAAAAAEAGABz76T2f2/VAXPvpPZ/b9UB1NBncjhp1QFQSwUGAAAAAAEAAQBaAAAALwAAAAAA"; // base64 content without mimeType

this.downloadFile(fileName, content);
like image 41
C. Draghici Avatar answered Oct 03 '22 01:10

C. Draghici