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

Downloading jpeg on Chrome iOS only results in 1) opening image in new tab or 2) downloading file named "document" with no extension #686

Open
jparismorgan opened this issue Dec 2, 2020 · 10 comments

Comments

@jparismorgan
Copy link

jparismorgan commented Dec 2, 2020

Hello, I am having trouble downloading an image on Chrome iOS 87.0.4280.77. I am using an iPhone 12 running iOS 14.3-beta. I am taking a screenshot with MediaRecorder and then creating a blob with const currentBlob = new Blob([buffer], {type: 'image/jpeg'}). My goal is to be able to trigger a download of the image, with the name and file extension set.

Issue: Testing with FileSaver.js with code from v2.0.4 or master

When I call saveAs(currentBlob, "name.jpg"), the file pops up in a new tab. I can long press on it and get the ability to save to photos (and other things), but I want a direct download.

For both versions of FileSaver.js I have tried changing the type of the blob (b/c I have read that may force Chrome to automatically download the file), but that results in no tab being opened and no download pop-up:

const currentBlob = new Blob([buffer], {type: 'image/jpeg'})
const rawBlob = new Blob([currentBlob])
const octetBlob = new Blob([currentBlob], {type: 'application/octet-stream'})
saveAs(blob / rawBlob / octetBlob, "name.jpg")

For contrast, Safari iOS shows the system dialog for a download in all three of these cases, with both versions of FileSaver.js.

Solution Attempt 1: Using a to download the image

The basic way to download a file is below. This results in the image being opened in a new window with no download prompt:

const currentBlob = new Blob([buffer], {type: 'image/jpeg'})
const a = document.createElement('a')
a.download = currentName
a.href = URL.createObjectURL(currentBlob)
a.rel = 'noopener'
document.body.appendChild(a)
click(a)
document.body.removeChild(a)

If I instead encode the Blob into a Base64 encoded string with readAsDataURL, I can get the file to download, but it is named 'document' and has no file extension (though if I rename the file to "name.jpg" then I can see it is in fact an image):

const currentBlob = new Blob([buffer], {type: 'image/jpeg'})
const reader = new FileReader()
reader.onloadend = () => {
    const a = document.createElement('a')
    a.download = currentName
    a.href = reader.result // I.e. data:image/jpeg;base64,/9j/4AA...
    a.rel = 'noopener'
    document.body.appendChild(a)
    click(a)
    document.body.removeChild(a)
}
reader.readAsDataURL(currentBlob)

alt text

alt text

I have also tried changing the Base64 encoded string in several ways to try to rename the file, but to no effect.

reader.onloadend = () => {
    ...
    const url = reader.result
    const urlWithHeaders = url.replace(';', `;name=${currentFilename};headers=Content-Disposition%3A%20attachment%3B%20filename=%22${currentFilename}%22;`)
    const urlFile = url.replace(/^data:[^;]*;/, 'data:attachment/file;')
    const urlOctet = url.replace('image/jpeg', 'binary/octet-stream')
    a.href = url / urlWithHeaders / urlFile / urlOctet
    ...
}

Solution Attempt 2: Using a new window

I have also tried by opening a new window, but it just opens the image in a new window and does not prompt a download:

const currentBlob = new Blob([buffer], {type: 'image/jpeg'})
const newWindow = window.open(URL.createObjectURL(currentBlob), '_blank')

Questions

  • Is Chrome iOS downloading supported with FileSaver.js?
  • Is this a known limitation of Chrome iOS?

There are several issues that seem potentially related, but all of them are quite old and haven't focused on images specifically: #506, #576, #343

I have also tried with an mp4 but, just like these questions describe, it is not working:

@mauriblint
Copy link

Having the same issue here with Chrome for iOS, any workaround?

@xiaoluntian
Copy link

xiaoluntian commented Jan 5, 2021

Hello, I am having trouble downloading an image on Chrome iOS 87.0.4280.77. I am using an iPhone 12 running iOS 14.3-beta. I am taking a screenshot with MediaRecorder and then creating a blob with const currentBlob = new Blob([buffer], {type: 'image/jpeg'}). My goal is to be able to trigger a download of the image, with the name and file extension set.

Issue: Testing with FileSaver.js with code from v2.0.4 or master

When I call saveAs(currentBlob, "name.jpg"), the file pops up in a new tab. I can long press on it and get the ability to save to photos (and other things), but I want a direct download.

For both versions of FileSaver.js I have tried changing the type of the blob (b/c I have read that may force Chrome to automatically download the file), but that results in no tab being opened and no download pop-up:

const currentBlob = new Blob([buffer], {type: 'image/jpeg'})
const rawBlob = new Blob([currentBlob])
const octetBlob = new Blob([currentBlob], {type: 'application/octet-stream'})
saveAs(blob / rawBlob / octetBlob, "name.jpg")

For contrast, Safari iOS shows the system dialog for a download in all three of these cases, with both versions of FileSaver.js.

Solution Attempt 1: Using a to download the image

The basic way to download a file is below. This results in the image being opened in a new window with no download prompt:

const currentBlob = new Blob([buffer], {type: 'image/jpeg'})
const a = document.createElement('a')
a.download = currentName
a.href = URL.createObjectURL(currentBlob)
a.rel = 'noopener'
document.body.appendChild(a)
click(a)
document.body.removeChild(a)

If I instead encode the Blob into a Base64 encoded string with readAsDataURL, I can get the file to download, but it is named 'document' and has no file extension (though if I rename the file to "name.jpg" then I can see it is in fact an image):

const currentBlob = new Blob([buffer], {type: 'image/jpeg'})
const reader = new FileReader()
reader.onloadend = () => {
    const a = document.createElement('a')
    a.download = currentName
    a.href = reader.result // I.e. data:image/jpeg;base64,/9j/4AA...
    a.rel = 'noopener'
    document.body.appendChild(a)
    click(a)
    document.body.removeChild(a)
}
reader.readAsDataURL(currentBlob)
alt text alt text

I have also tried changing the Base64 encoded string in several ways to try to rename the file, but to no effect.

reader.onloadend = () => {
    ...
    const url = reader.result
    const urlWithHeaders = url.replace(';', `;name=${currentFilename};headers=Content-Disposition%3A%20attachment%3B%20filename=%22${currentFilename}%22;`)
    const urlFile = url.replace(/^data:[^;]*;/, 'data:attachment/file;')
    const urlOctet = url.replace('image/jpeg', 'binary/octet-stream')
    a.href = url / urlWithHeaders / urlFile / urlOctet
    ...
}

Solution Attempt 2: Using a new window

I have also tried by opening a new window, but it just opens the image in a new window and does not prompt a download:

const currentBlob = new Blob([buffer], {type: 'image/jpeg'})
const newWindow = window.open(URL.createObjectURL(currentBlob), '_blank')

Questions

  • Is Chrome iOS downloading supported with FileSaver.js?
  • Is this a known limitation of Chrome iOS?

There are several issues that seem potentially related, but all of them are quite old and haven't focused on images specifically: #506, #576, #343

I have also tried with an mp4 but, just like these questions describe, it is not working:

Do you have a try to add the response header -----Content-Disposition:attachment;filename='xxx.xxx'?

@av01d
Copy link

av01d commented Jul 26, 2021

This issue is still present today it seems? Latest Chrome on latest iPadOS.
How can we set the filename of a file download in Chrome for iOS?

@av01d
Copy link

av01d commented Jul 26, 2021

Do you have a try to add the response header -----Content-Disposition:attachment;filename='xxx.xxx'?

Do you understand the issue? How do you set a Content-Disposition header in client side javascript?

@jparismorgan
Copy link
Author

jparismorgan commented Jul 26, 2021

@av01d I think @xiaoluntian is referring to something like urlWithHeaders, which if you read the Solution Attempt 1 section above I mention trying unsuccessfully:

I have also tried changing the Base64 encoded string in several ways to try to rename the file, but to no effect.
reader.onloadend = () => {
...
const url = reader.result
const urlWithHeaders = url.replace(';', ;name=${currentFilename};headers=Content-Disposition%3A%20attachment%3B%20filename=%22${currentFilename}%22;)
const urlFile = url.replace(/^data:[^;]*;/, 'data:attachment/file;')
const urlOctet = url.replace('image/jpeg', 'binary/octet-stream')
a.href = url / urlWithHeaders / urlFile / urlOctet
...
}

But I don't remember much of what I tried beyond what is written above, so you may want to give it a go yourself, @av01d.

To answer your original question @xiaoluntian, yes, I tried setting a header and it didn't work 😕 . Is the code snippet above how you suggest doing it?

@Gbahdeyboh
Copy link

This issue still persists, anyone found a fix?

@vanga
Copy link

vanga commented Feb 23, 2022

It is working in chrome after setting the Content Disposition response header.

@jparismorgan
Copy link
Author

@vanga Could you share example code? Thanks!

@vanga
Copy link

vanga commented Mar 4, 2022

@jparismorgan My images are stored in S3, so I just set the metadata so that content disposition response header is sent.
I have set it to Content-disposition: attachment like suggested in few of the above comments in this thread.

@Ismael-S
Copy link

Ismael-S commented Jul 4, 2023

Found a solution for setting the filename and extension, (had this problem with various browsers when obtaining the base64 dynamically). I had to add target="_blank".

On Solution 1, before appending the element to the body, add:
a.target = '_blank'

I'm not using this library, but my solution is the same as Solution 1, so hopefully it helps you.

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