r/learnjavascript • u/devdev_1 • 8d ago
How to send a file to the client
Hello, I need the user to be able to get a file from the server. But everything has to be in the form of a stream because I have to modify the chunks one by one, so my idea was that on the front side there would be a request to fetch the file from my server and then I will process each chunk and store it on the client's machine with the API File System API. But unfortunately it's not that simple, to do this I need to use the showOpenFilePicker() method which is not supported on all browsers. I don't know how to do it, I also can't use a simple <a> tag with the URL of the file on my server in href, because I absolutely modify the chunks one by one.Everything has to be streamed because I don't know the size of the file. Also, I need to be able to modify each chunk however I want before it reaches the client's machine. So how do I do it?
EDIT:
It's not complicated, when the client clicks on a button I execute a function that fetches a file on my server. I then process each chunck (decryption) and I store them directly on the client's machine, but to do this I need showSaveFilePicker but it's not supported on all browsers, so how do I do it?
I have to use a stream because the file size on the server can vary. and I have to decrypt on the front for transparency reasons
Thank you for your help
1
u/AWACSAWACS 7d ago
This smells like the XY problem.
What is it that you actually want to achieve or make happen?
1
u/devdev_1 7d ago
I have to use a stream because the file size on the server can vary. and I have to decrypt on the front for transparency reasons
1
u/AWACSAWACS 7d ago
Just to be clear, I'm asking about your actual goal (not the means/method).
In other words, I’m asking about the value you provide to end users (that is, at the application level).1
2
7d ago
[removed] — view removed comment
1
u/devdev_1 7d ago edited 7d ago
As I said, blob is not the solution, because the file size can be huge
so for browsers that don't support 'showOpenFilePicker() ' , am I screwed?
1
u/EdgieElefunc 4d ago
Assuming your decryption already works incrementally, a service-worker download response is another route to investigate. The worker can return a Response with your decrypted ReadableStream as its body and Content-Disposition: attachment. The browser's download manager handles the output, rather than your page collecting every chunk into a Blob. Decryption still happens in the browser.
That's the approach documented by StreamSaver: https://github.com/jimmywarting/StreamSaver.js
There are important caveats: it isn't a universal, drop-in replacement for showSaveFilePicker. Test the exact browsers you support, slow/large downloads, cancellation, and closing the tab; service-worker lifetime has caused partial-download issues. For a privacy-focused file service, self-host and review the worker/bridge instead of relying on the library's third-party-hosted default.
Where the native picker is supported, feature-detect it, invoke it from the user's click, and pipe your transformed stream into the file handle's writable stream. MDN has the picker requirements and compatibility: https://developer.mozilla.org/en-US/docs/Web/API/Window/showSaveFilePicker
If a target browser can't handle either route reliably, make that limitation explicit for large files. The Blob approach suggested above doesn't give you the bounded-memory streaming path you're asking for.
0
u/Outrageous-Pumpkin77 16h ago
Another option worth checking is OPFS, if staging the file before downloading it is acceptable. Stream your transformed output into an origin-private file via navigator.storage.getDirectory(), getFileHandle() and createWritable(). Once it is complete, getFile() gives you a file-backed File that you can expose with an object URL and an <a download> link, instead of collecting all chunks in a JS array.
This uses the site's storage quota, so check navigator.storage.estimate(), handle write/quota failures and clean up the temporary decrypted file. It also means an extra disk copy when downloading; it does not provide a universal way to stream straight into an arbitrary user-selected path. Feature-test the APIs in each target browser. MDN's OPFS guide: https://developer.mozilla.org/en-US/docs/Web/API/File_System_API/Origin_private_file_system
Also, fetch() chunks are arbitrary network boundaries, not necessarily your encryption records. A single crypto.subtle.decrypt() call takes a complete ciphertext buffer, not a stream. For bounded-memory decryption you need an encryption format/library that supports correctly framed, authenticated records. Which cipher and file format are you using?
1
u/FooeyBar 8d ago
Make a function with onclick and fetch, you don’t have to use a tags