I'd like to set the file name and format of a "non-typical file", for a lack of better words. The files are stored at locations such as the following...
let link = https://.../videoplayback?ipbits=0&initcwndb...D134213BA9465CB74DFD36CDE47BF.102638C4A9F3ACA357F79EE747DD5F49F1E0F0DE
When one downloads such files, even if they are pletely different files, they always end up being saved with the same name and never a file extension.
I've tried url.download = link + '.mp4'
, but this has no effect whatsoever.
How can I do this?
I'd like to set the file name and format of a "non-typical file", for a lack of better words. The files are stored at locations such as the following...
let link = https://.../videoplayback?ipbits=0&initcwndb...D134213BA9465CB74DFD36CDE47BF.102638C4A9F3ACA357F79EE747DD5F49F1E0F0DE
When one downloads such files, even if they are pletely different files, they always end up being saved with the same name and never a file extension.
I've tried url.download = link + '.mp4'
, but this has no effect whatsoever.
How can I do this?
Share Improve this question asked Jul 26, 2017 at 3:11 oldboyoldboy 5,9747 gold badges42 silver badges99 bronze badges 8- How are you sourcing the files? – TheChetan Commented Jul 28, 2017 at 8:13
- @TheChetan downloading them off of the internet via the browser – oldboy Commented Jul 28, 2017 at 22:48
-
@Anthony Are the files served with
CORS
headers? – guest271314 Commented Jul 30, 2017 at 13:47 - @guest271314 i have no idea. how would i even determine that? ajax or something? – oldboy Commented Aug 1, 2017 at 3:35
- @Anthony Do you have control over the files? – guest271314 Commented Aug 1, 2017 at 3:38
3 Answers
Reset to default 6 +25According to the MDN for anchor tags with the download attribute:
Can be used with blob: URLs and data: URLs, to make it easy for users to download content that is generated programmatically using JavaScript (e.g. a picture created using an online drawing Web app).
If the HTTP header Content-Disposition: is present and gives a different filename than this attribute, the HTTP header has priority over this attribute.
If this attribute is present and Content-Disposition: is set to inline, Firefox gives priority to Content-Disposition, like for the filename case, while Chrome gives priority to the download attribute.
This attribute is only honored for links to resources with the same-origin.
So if you are dynamically generating these links and they e from your own server - you can set the filename. A cross-origin request will not work!
You may be able use ajax to fetch the file as a blob and trick the browser into thinking that the data is not cross-origin. See this answer for one possible implementation.
One approach would be to perform a HEAD
request before setting .download
property of <a>
element to determine the Content-Type
of the requested resource, use a JSON
string, JavaScript object or other key, value pair data storage format to reflect pairs of valid MIME
types to valid file extensions corresponding to the MIME
type property. Use .indexOf()
, RegExp
or other filtering method to determine if there is a match between the property name of the string or object and the value of the property, if true store the extension value and concatenate the extension to the suggested file name.
If the file is not served with CORS headers you can try using a proxy to make HEAD
of GET
request to get the Content-Type
header before setting the .download
attribute.
It should be noted that the .download
attribute is only a suggestion to the user for the filename. The user can change the name of the file at any time, including deleting the file extension, for whatever reason they may or may not have. Or not download the resource at all.
const mimeTypeExtensions = {
"text/plain": ".txt",
"video/mp4": ".mp4",
/* valid MIME types and corresponding extensions */
}
const a = document.querySelector("a");
a.addEventListener("click", event => {
if (!a.download) {
event.preventDefault();
fetch(a.href, {method: "HEAD"})
.then(response => {
const mimeType = response.headers.get("content-type");
let fileExtension = "";
for (let [key, prop] of Object.entries(mimeTypeExtensions)) {
if (key.indexOf(mimeType) > -1) {
fileExtension = prop;
break;
};
}
a.download = `filename${fileExtension}`;
a.click();
})
.catch(err => console.error(err))
}
});
<a href="https://gist.githubusercontent./guest271314/11edc4566ba94f204dd46e6ae26edaad/raw/d118e99abbe2a2c60634e46816df9e1b9de6b6b8/D134213BA9465CB74DFD36CDE47BF102638C4A9F3ACA357F79EE747DD5F49F1E0F0DE">click</a>
Here is an easy option if you don't mind adding a library. FileSaver.js handles these cases well while hiding the ugly details. I was going to go into how you could create Blob, write the contents of the download into it, and then create an anchor tag with an Object URL created from the Blob, but FileSaver handles that already and you could look at the source if you really wanted.
Just modify the following to add some checks and logic for determining filename/filetype and you should be able to tackle your use case.
<script src="cdn-to-FileSaver.js"></script>
<script>
var url = ...;
fetch(url).then((response) => {
var blob = response.blob();
var filename = 'song.mp4';
saveAs(blob, filename);
});
</script>