FileSaver.js
FileSaver.js copied to clipboard
Downloading jpeg on Chrome iOS only results in 1) opening image in new tab or 2) downloading file named "document" with no extension
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)
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: https://github.com/eligrey/FileSaver.js/issues/506, https://github.com/eligrey/FileSaver.js/issues/576, https://github.com/eligrey/FileSaver.js/issues/343
I have also tried with an mp4 but, just like these questions describe, it is not working:
- https://stackoverflow.com/questions/63848034/anchor-download-attribute-created-with-javascript-not-working-on-ios-chrome
- https://stackoverflow.com/questions/64969672/unable-to-download-files-with-the-correct-name-in-chrome-on-ios
Having the same issue here with Chrome for iOS, any workaround?
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
ormaster
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 imageThe 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)
![]()
![]()
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:
- https://stackoverflow.com/questions/63848034/anchor-download-attribute-created-with-javascript-not-working-on-ios-chrome
- https://stackoverflow.com/questions/64969672/unable-to-download-files-with-the-correct-name-in-chrome-on-ios
Do you have a try to add the response header -----Content-Disposition:attachment;filename='xxx.xxx'?
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?
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?
@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?
This issue still persists, anyone found a fix?
It is working in chrome after setting the Content Disposition response header.
@vanga Could you share example code? Thanks!
@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.
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.
Is there a solution to this yet? It's been 4 years, I'm hoping something has happened.
Changing the delivery from s3 isn't a viable option, as I want it to display in the browser nomrally, but when they click a download button, I want to download the image.