Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Problem with File.Download.bytes on Mobile Safari and Chrome #17

Open
jenfan opened this issue Mar 17, 2019 · 8 comments
Open

Problem with File.Download.bytes on Mobile Safari and Chrome #17

jenfan opened this issue Mar 17, 2019 · 8 comments

Comments

@jenfan
Copy link

jenfan commented Mar 17, 2019

I just copied good SSCCE example from #10

import Bytes.Encode as E
import File.Download as Download

main =
  Platform.worker
    { init = \() -> ( (), Download.bytes "word.txt" "text/plain" bytes )
    , update = \_ _ -> ( (), Cmd.none )
    , subscriptions = \_ -> Sub.none
    }

bytes =
  E.encode <| E.sequence <|
    List.map E.unsignedInt8 [ 101, 115, 115, 101, 110, 116, 105, 97, 108 ]

I started server with code above, went to it using mobile browsers (safari and crhome on iphone 7) and get weird errors, like trying to download files from server

here screenshots, Im sorry they are on russian, at least I can write translation of this :)
Safari is unabled to open page
Error: could not complete operation (WebkitBlobResource, error 1)
IMAGE 2019-03-17 23:16:48
unable to access site page temporarily unavailable or permanently moved to new address
IMAGE 2019-03-17 23:16:57
(tested on iphone 7)

And I cant figure out a way to save pdf file in my app for mobile users. The pdf file saves from server response after uploading form, (then it's generating and send back), so I dont have permanent link to file for easy way.
I will very appreciate for fixing this. Thank you!

@billstclair
Copy link

billstclair commented Mar 31, 2019

I'm seeing the same behavior. If I comment out the call to URL.revokeObjectURL in src/Elm/Kernel/File.js, then the blob (an image in my app) will display in the browser. But it doesn't download.

It appears that the download attribute is not supported in mobile Safari. From the debugger console:

> node = document.createElement('a')
< <a></a>
> node.download = 'foo.jpg'
< "foo.jpg"
> node.href = 'bar.jpg'
< "bar.jpg"
> node
< <a href="bar.jpg"></a>

In desktop Safari (and Chrome and Firefox), the same code produces:

> node = document.createElement('a')
< <a></a>
> node.download = 'foo.jpg'
< "foo.jpg"
> node.href = 'bar.jpg'
< "bar.jpg"
> node
< <a download="foo.jpg" href="bar.jpg"></a>

This is even easier to see by creating a file with the following HTML:

<a href='image.jpg' download>image.jpg</a>

Where image.jpg is a JPEG file in the same directory as the file containing that HTML.

Clicking on that link in mobile Safari opens the image in a page. Clicking on it in a desktop browser downloads the JPEG file.

@robinheghan
Copy link

Is this still a problem on iOS 13? Pre-iOS 13, the download attribute was not supported by mobile safari.

@billstclair
Copy link

My tiny HTML snippet above now works in Safari under iOS 13. It offers to download or view the image when you click the link. It does NOT work in the latest Chrome or Brave under iOS 13.

@darkensk
Copy link

darkensk commented Dec 6, 2019

I have encountered this problem in Safari under iOS13.1. New page opens with the error message.

When I've tried this snippet in console on the device and it works as expected, context window opens with options to view/download:

 var jsonObject = {"name": "John", "age": 30, "car": null};
 var textFileAsBlob = new Blob([jsonObject], {type: 'text/plain'});
 var downloadLink = document.createElement("a");
 downloadLink.download = "file.json";
 downloadLink.href = window.webkitURL.createObjectURL(textFileAsBlob);
 downloadLink;
 //<a download="file.json" href="blob:https://localhost:3010/349f22ce-9b8a-44f6-9c56-9127503f6e65"/a>
 downloadLink.click();

(From https://stackoverflow.com/a/49990108/6664068 )
From my understanding it's more or less the same as what's going on in the src/Elm/Kernel/File.js without the URL.revokeObjectURL...

@richtlu
Copy link

richtlu commented Dec 11, 2019

I have encountered this problem in Safari under iOS13.1. New page opens with the error message.

When I've tried this snippet in console on the device and it works as expected, context window opens with options to view/download:

 var jsonObject = {"name": "John", "age": 30, "car": null};
 var textFileAsBlob = new Blob([jsonObject], {type: 'text/plain'});
 var downloadLink = document.createElement("a");
 downloadLink.download = "file.json";
 downloadLink.href = window.webkitURL.createObjectURL(textFileAsBlob);
 downloadLink;
 //<a download="file.json" href="blob:https://localhost:3010/349f22ce-9b8a-44f6-9c56-9127503f6e65"/a>
 downloadLink.click();

(From https://stackoverflow.com/a/49990108/6664068 )
From my understanding it's more or less the same as what's going on in the src/Elm/Kernel/File.js without the URL.revokeObjectURL...

Working smoothly on iOS. Have you inject your snippet into File.js?

Also it could be potential memory leak (https://stackoverflow.com/questions/49209756/do-i-always-need-to-call-url-revokeobjecturl-explicitly). How about revoke it later?

@richtlu
Copy link

richtlu commented Dec 16, 2019

You can postpone revokingObjectURL by

setTimeout(function(){
    URL.revokeObjectURL(objectUrl);
});

There is probably required time which iOS needs after click event and the object cannot be revoke before it.

richtlu added a commit to richtlu/file that referenced this issue Dec 16, 2019
Fixing iOS Safari blob immediate revoking. 
elm#17
@ollef
Copy link

ollef commented Aug 4, 2020

Here's a roundabout way that we used to apply the above patch without having to change the File library, which seems to work for us. Run the following code at startup:

const originalRevokeObjectURL = URL.revokeObjectURL;
URL.revokeObjectURL = function () {
  setTimeout(() => {
    originalRevokeObjectURL.apply(URL, arguments);
  });
};

This could unfortunately affect other functionality that uses URL.revokeObjectURL, but we didn't use it elsewhere.

@Massolari
Copy link

Here's a roundabout way that we used to apply the above patch without having to change the File library, which seems to work for us. Run the following code at startup:

const originalRevokeObjectURL = URL.revokeObjectURL;
URL.revokeObjectURL = function () {
  setTimeout(() => {
    originalRevokeObjectURL.apply(URL, arguments);
  });
};

This could unfortunately affect other functionality that uses URL.revokeObjectURL, but we didn't use it elsewhere.

I had to use setTimeout with 200ms, without it, it didn't work for me:

const originalRevokeObjectURL = URL.revokeObjectURL
URL.revokeObjectURL = function () {
  setTimeout(() => {
    originalRevokeObjectURL.apply(URL, arguments)
  }, 200)
}

rupertlssmith pushed a commit to elm-janitor/file that referenced this issue Mar 4, 2022
Update File.js

Fixing iOS Safari blob immediate revoking.
elm#17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants