eligrey / FileSaver.js

An HTML5 saveAs() FileSaver implementation
https://eligrey.com/blog/saving-generated-files-on-the-client-side/
Other
21.42k stars 4.38k forks source link

Retrieving File, Modifying, then downloading with FileSaver corrupts non-plaintext files #745

Open chiahsoon opened 2 years ago

chiahsoon commented 2 years ago

Hi! I'm currently using FileSaver ^2.0.5, and my use case is for my react app to fetch an encrypted blob (from a server that's not owned by us), decrypt it, then allow the user to download the file.

Here is how I'm fetching the encrypted blob:

export const downloadFile = async (sourceParams: Params, fileId: number): Promise<ArrayBuffer> => {
    let url = `${sourceParams.siteUrl}/api/access/datafile/${fileId}`;
    url += `?key=${sourceParams.apiToken}`;
    const resp = await fetch(url);
    const data = await resp.blob();
    return await data.arrayBuffer();
};

And here is how I'm decrypting it and managing the download:

const decryptAndDownload = async (sourceParams: DataverseSourceParams, fileId: number, fileName: string, salt: string, password: string): Promise<void> => {
    const fileBuf = await downloadFile(sourceParams, fileId); // Shown above
    const decryptedData = decryptWithPassword(fileBuf, password, salt); // decryptedData is a string of binary representation
    const blob = new Blob([decryptedData], {
        type: 'application/octet-stream;charset=utf-8;',
    });
    saveAs(blob, fileName);
};

For files like .txt and .xlsx, it seems to work just fine. Trying .xlsx and .pdf results in a larger than expected file size (which I've observed to be after converting the file to a blob) and results in the downloaded file becoming corrupted.

Further, in my case, I'm unable to allow users to download directly from my server because the encryption process has to be done client-side and the decrypted file should not reach any server. I've also tried alternative ways like creating a <a></a> element in the DOM to manage the download but it fails as well.

Any help is greatly appreciated! :)