JavaScript: post for download

I want to download an encrypted file from my server, decrypt it and save it locally. I want to decrypt the file and write it locally since it is loading, and not wait for the download to complete, decrypt and then decrypt the file in the anchor tag. The main reason I want to do this is that with large files, the browser does not need to store hundreds of megabytes or several gigabytes in memory.

+6
source share
2 answers

This will be possible only with a combination of a service worker + fetch + stream. Some browsers have a working and selective modes, but even less support fetching with streaming (Blink)

new Response(new ReadableStream({...}))

I created a streaming file saver library to communicate with another service worker to intercept a network request: StreamSaver.js

This is a little different from the node thread, here is an example

 function unencrypt(){ // should return Uint8Array return new Uint8Array() } // We use fetch instead of xhr that has streaming support fetch(url).then(res => { // create a writable stream + intercept a network response const fileStream = streamSaver.createWriteStream('filename.txt') const writer = fileStream.getWriter() // stream the response const reader = res.body.getReader() const pump = () => reader.read() .then(({ value, done }) => { let chunk = unencrypt(value) // Write one chunk, then get the next one writer.write(chunk) // returns a promise // While the write stream can handle the watermark, // read more data return writer.ready.then(pump) ) // Start the reader pump().then(() => console.log('Closed the stream, Done writing') ) }) 

There are also two other ways to get a stream response using xhr, but it is not standard and does not matter if you use them (responseType = ms-stream || moz-chunked-arrayBuffer), because StreamSaver depends on fetch + ReadableStream by any method and cannot be used in any other way

You can do something similar later when WritableStream + Transform streams are also implemented

 fetch(url).then(res => { const fileStream = streamSaver.createWriteStream('filename.txt') res.body .pipeThrogh(unencrypt) .pipeTo(fileStream) .then(done) }) 

It is also worth mentioning that the default download manager is usually associated with background loading, so ppl sometimes closes the tab when it sees a download. But all this happens in the main thread, so you need to warn the user when he leaves

 window.onbeforeunload = function(e) { if( download_is_done() ) return var dialogText = 'Download is not finish, leaving the page will abort the download' e.returnValue = dialogText return dialogText } 
+5
source

For security reasons, browsers do not allow directing the incoming read stream directly to the local file system, so you have 2 ways to solve this problem:

  1. window.open(Resourse_URL) : load the resource in a new window with Content_Disposition is set to "attachment";
  2. <a download href="path/to/resourse"></a> : using the "download" attribute AnchorElement to download the stream to the hard drive;

hope this helps :)

0
source

Source: https://habr.com/ru/post/989920/


All Articles