jimmywarting / StreamSaver.js

StreamSaver writes stream to the filesystem directly asynchronous
https://jimmywarting.github.io/StreamSaver.js/example.html
MIT License
4.04k stars 418 forks source link

Sw does'nt work on Firefox / Ubuntu & Windows #153

Open rhanka opened 4 years ago

rhanka commented 4 years ago

On firefox 75 (edited: Ubuntu 18.04), the example does'nt work on fresh start (after a Shift+Ctrl+R) : https://jimmywarting.github.io/StreamSaver.js/examples/saving-a-blob.html

Same problem on all examples : on first load, streamSaver.createWriteStream don't popup the file download window. It seems the service worker isn't started.

It works fine on Chrome.

jimmywarting commented 4 years ago

just tried firefox 75 on mac, can't reproduce. did you try it in private mode? service worker ain't available in private mode... but it should fall back to using blob builder and then a[download]

rhanka commented 4 years ago

I confirm there is no problem on Firefox 75 on mac. The problem occurs on Ubuntu (18.04), after a Ctrl+shift+R, but works after a Ctrl+R. The problems occurs on the same way on Window 10 (after a full qualification on standard a standard windows with firefox 75).

Sorry for that again, but where to find a use case on : "blob builder in a a[download]" (the example provided for blob builder don't work, and work with a button not a download).

jimmywarting commented 4 years ago

There is no real "blobBuilder" i use it as a term for when useBlobFallback is true. It reads the hole stream and constructs a blob, then it creates a link and use the download attribute https://github.com/jimmywarting/StreamSaver.js/blob/a72227c1950dcc88088aebcf9e6b664ae81992ea/StreamSaver.js#L276-L281

rhanka commented 4 years ago

Ctrl+Shift+R doesn't systematically go to an error but >50% on linux and windows.

using https://jimmywarting.github.io/StreamSaver.js/examples/saving-a-blob.html, here is the "error" sequence. just after clicking:

So I think maybe for a time sequence problem the service worker don't catch the sample request.

An acceptable workaround could be to have a way to catch the 404 error somewhere, then fall back to any other method. I tried on my code, but i didn't find a way to get and error code.

jimmywarting commented 4 years ago

Remember i had some similar issue before (unrelatet to streamsaver - tried making a service worker rendering) And i could not get the made up urls from service workers so i added self.clients.claim() to make it work.

self.addEventListener('install', () => {
  self.skipWaiting()
})

self.addEventListener('activate', event => {
  event.waitUntil(self.clients.claim())
})

could there maybe be some interferences with skipWaiting? some kind of bad race condition?

Maybe i'm sending the postMessage from the mitm.html to the sw.js too early? maybe i have to wait for some controllerchange event on navigator.serviceWorker before forwarding the postMessage to the sw.js?

rhanka commented 4 years ago

It seems indeed that the problem is not due purely to streamsaver but that the url isn't ready when the second iframe is created. when i im stuck in this case the url does open when entered manually in another tab (which is kind of frustrating!).

Moreover in this case, successive downloads all fail in the same way (404 on every new iframe).

So the "sending the postMessage... to early" seems to be the point, perhaps in the sw.js near :

map.set(downloadUrl, metadata)
port.postMessage({ download: downloadUrl })

but map.set doesn't seems to need to be awaited...

For now I had to make a very bad thing as a workaround : need to add useBlobFallBack exposed in streamSaver.useBlobFallBack, and add a function streamSaver.onmessage to have status of the download outside of streamSaver. Adding those two methods I am able to handle the fallback automatically : if downloads doesn't start in some time (200ms), then I activate useBlobFallBack. This seems to work, but i would have to ask to expose those two methods and this is prehaps not a good thing on the long time...

jimmywarting commented 4 years ago

hmm

wouldn't want to expose useBlobFallBack if i don't have to... would rather want to find a solution. it's better to write to the disc directly instead of buffer up everything into memory.

It seems indeed that the problem is not due purely to streamsaver

it could maybe be with how the browser handles the claim incorrectly? maybe the service worker only "claim" it's own domain - meaning that the mitm.html can start downloading but not your web page?

just tried FF on mac and did cmd+shift+r (same problem - now i can also help out and debug) funny thing was when i have dev tool open and hit cmd+shift+r then it works just fine but not when devtool is closed.

jimmywarting commented 4 years ago

Seems like there is some issues along with service worker claim in firefox... https://bugzilla.mozilla.org/buglist.cgi?quicksearch=service+worker+claim

I tried open the 2nd download iframe later, did it inside mitm, tried running clients.claim() manually in the inspector before loading the iframe, nothing did work... the controllerchange event never fired. this Immediate Claim Demo did not work either when doing a "hard" reload

all that did work was opening up the url in a new tab. so i fought about doing a redirect:

-            makeIframe(evt.data.download)
+            location = evt.data.download

and it did seem to work. Not so fond of it doe. it has other problems too like calling

(other solutions could be to open up a new popup)

jarodium commented 4 years ago

Hello

In Macos with FF 76 I get these issues from the browser:

image

The first message is translated as "Failed to get service worker record (s): Access to storage is restricted in this context due to user settings or private browsing mode." I don't recall ever starting FF in private mode or changed any settings that would influence any storage behavior. Rarely use the browser so it's pretty much default settings.

denis111 commented 4 years ago

For me in FF on Ubuntu in private mode the file doesn't appear in "downloads" but it's actually downloading in background and then open/save dialog appears when download is finished without any feedback about download progress then...

Yahweasel commented 4 years ago

I want to quickly add a small extra detail to the original problem: Although it does feel like some kind of race condition, since it only pops up randomly, simply delaying before making the downloading iframe doesn't work. There's probably some more subtle ordering violation happening in Firefox. For the time being, to make StreamSaver "work", I've stubbed out support for everything but the blob fallback on Firefox, as at least that works reliably.

Yahweasel commented 4 years ago

Spurred on by your suggestion that it seemed to work with an actual navigation but not an iframe, I added this truly grotesque workaround:

            if (navigator.userAgent.indexOf("Firefox") >= 0)
              makePopup(evt.data.download)
            else
              makeIframe(evt.data.download)

It does seem to "work", albeit it obviously gives the usual warnings about popup windows. I can't swap it out for navigation in my context, because I have ongoing events and connections that would be disconnected.

jimmywarting commented 4 years ago

I think i may have found a "better" solution https://stackoverflow.com/a/60133005/1008999 I have used it in another project of mine and it works 👌