Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Set file name and format if file or file location contains neither

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 completely 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?

like image 310
oldboy Avatar asked Jul 26 '17 03:07

oldboy


2 Answers

According 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 come 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.

like image 81
Kwestion Avatar answered Sep 23 '22 14:09

Kwestion


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.com/guest271314/11edc4566ba94f204dd46e6ae26edaad/raw/d118e99abbe2a2c60634e46816df9e1b9de6b6b8/D134213BA9465CB74DFD36CDE47BF102638C4A9F3ACA357F79EE747DD5F49F1E0F0DE">click</a>
like image 30
guest271314 Avatar answered Sep 20 '22 14:09

guest271314