Closed tmcw closed 2 years ago
I ran into the problems you described which prompted me to open the PRs #101 and #102.
My current strategy, using the changes of both PRs, is to show the dialog as fast as possible, so no computations are taking place between the user interaction and showing the dialog. But with the new callback introduced in #102 I can trigger the long-running operations (which actually "generate" the data to be saved) right after the dialog was shown. An important part for me is to only start the data generation after the dialog was closed (and not cancelled).
But this only works in browsers supporting the new API. From what I understand of the old API, the whole data has to be assembled in a Blob before showing the dialog.
Thanks for working on this. I'll take a look at the PRs soon (busy with urgent Google I/O stuff at the moment), but meanwhile please keep in mind that all solutions need to work with the old way of <input type=file>
as well.
Closed via #101 and #102. Thanks!
@tmcw @tclangv: Please give the just released v0.27.0
a shot. Thanks both for your work!
I'm not totally sure how @tclangv has been doing this - are you also creating a new Response
object to feed into the save command?
Edit: yeah, still not sure if it's possible to cleanly implement a solution here. I'm not yet able to get any solution that works without creating a fake Response type, and that breaks in Firefox.
Sorry to be a nag, but it really seems like the legacy integration simply doesn't work with readable streams or even native responses.
Here's a reduced testcase of the streamToBlob method:
https://observablehq.com/d/5e661dd48af858a4
In Chrome this produces
TypeError: Failed to execute 'releaseLock' on 'ReadableStreamDefaultReader': Cannot release a readable stream reader when it still has outstanding read() calls that have not yet settled
In firefox it produces
AbortError: The operation was aborted.
In Safari this produces
TypeError: TypeError: read() called on a reader owned by no readable stream
I think the root cause is
await res.blob()
is what causes the stream to be read, but it is being called after reader.releaseLock
, which closes the stream for reading.
PR in https://github.com/GoogleChromeLabs/browser-fs-access/pull/104
I'm not totally sure how @tclangv has been doing this - are you also creating a
new Response
object to feed into the save command?Edit: yeah, still not sure if it's possible to cleanly implement a solution here. I'm not yet able to get any solution that works without creating a fake Response type, and that breaks in Firefox.
Yes, I create a Response object and yes, it only works with the new API. It seems to me that there might is no hope for Firefox support under the constraints I have. But I am not well versed in JS, so don't take my word for it. For the time being I have given up on Firefox though.
@tmcw @tclangv: Please give the just released
v0.27.0
a shot. Thanks both for your work!
Works fine for me! And thanks for the quick release as well!
Bit of a doozy here…
Okay, so at least Chrome has a rule that calls to APIs like
showSaveFilePicker
have to be loosely connected to a "gesture" like a click. I don't know exactly what the cutoff is, 1 second works but 5 seconds shows an error that the file picker needs to be opened by a gesture.So, presuming you have a save operation like an "export" and that export has to do some complex things, like converting files in a webworker or dynamically loading some extra modules for conversion. You can accidentally go over the invisible and only-barely-mentioned tripline and Chrome will consider your
showSaveFilePicker
call to be invalid. Such is life.So browser-fs-access currently accepts Blob or Response as input, and it pipes either into a WritableStream.
You can, currently, use the Response support as a workaround by creating your own Response object, however this then triggers code that assumes the Response is real and the control of your mime types and extensions changes dramatically in this section of code
https://github.com/GoogleChromeLabs/browser-fs-access/blob/c99a348b14b0ea4cc3c1e84ded89c8bab19b7436/src/fs-access/file-save.mjs#L40
At least for me, this causes a lot of problems:
Response.type
isdefault
, which means that we add adefault
type to the accept parameter of showSaveFilePicker, which prevents it from running.So: q, could or should browser-fs-access allow users to provide a ReadableStream as a third option so that long-running file save operations are possible without having to worry about the semantics of wrapping that stream in a Response object?