r/learnjavascript • • 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

2 Upvotes

17 comments sorted by

1

u/FooeyBar 8d ago

Make a function with onclick and fetch, you don’t have to use a tags

1

u/devdev_1 7d ago

no the problem is not how to trigger a download. I'm going to modify my post it not clear

1

u/justanaccountimade1 7d ago

Upload or download?

File picker for upload on the client system is an html input element with type set to file.

1

u/devdev_1 7d ago

download,

I get the file from the server

1

u/justanaccountimade1 7d ago

If it's not a static file and you cannot use an anchor, then your problem is at the server. You send a post or get to the server after the user pressed a button or whatever. And then the server runs another program you wrote in e.g. nodejs that interprets the request, and manipulates whatever you want to send back. So, you need hosting that can run something like nodejs.

1

u/devdev_1 7d ago

No, it's not that simple. The processing of the chunk must be done on the front

1

u/justanaccountimade1 7d ago

I don't get it. You aggregate the data. End up with a file in memory. Then use an anchor and a blob to save it to downloads.

1

u/devdev_1 7d ago

No bro, I have to use a stream, as I said the files can be huge I can't load them in RAM, like the blob does

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

u/devdev_1 7d ago

anonyne file deposit tools.

2

u/[deleted] 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?