Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get file name from content-disposition

I downloaded a file as response of ajax. How to get the file name and file type from content-disposition and display thumbnail for it. I got many search results but couldn't find right way.

$(".download_btn").click(function () {
  var uiid = $(this).data("id2");

  $.ajax({
    url: "http://localhost:8080/prj/" + data + "/" + uiid + "/getfile",
    type: "GET",
    error: function (jqXHR, textStatus, errorThrown) {
      console.log(textStatus, errorThrown);
    },
    success: function (response, status, xhr) {
      var header = xhr.getResponseHeader('Content-Disposition');
      console.log(header);     
    }
});

Console output:

inline; filename=demo3.png
like image 295
Arun Sivan Avatar asked Dec 02 '16 19:12

Arun Sivan


People also ask

What is content disposition filename?

Content-Disposition is an optional header and allows the sender to indicate a default archival disposition; a filename. The optional "filename" parameter provides for this. This header field definition is based almost verbatim on Experimental RFC 1806 by R. Troost and S.

What is the use of content disposition header?

In a regular HTTP response, the Content-Disposition response header is a header indicating if the content is expected to be displayed inline in the browser, that is, as a Web page or as part of a Web page, or as an attachment, that is downloaded and saved locally.


5 Answers

Here is how I used it sometime back. I'm assuming you are providing the attachment as a server response.

I set the response header like this from my REST service response.setHeader("Content-Disposition", "attachment;filename=XYZ.csv");

function(response, status, xhr){
    var filename = "";
    var disposition = xhr.getResponseHeader('Content-Disposition');
    if (disposition && disposition.indexOf('attachment') !== -1) {
        var filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
        var matches = filenameRegex.exec(disposition);
        if (matches != null && matches[1]) { 
          filename = matches[1].replace(/['"]/g, '');
        }
    }
}

EDIT: Editing the answer to suit your question- use of the word inline instead of attachment

function(response, status, xhr){
    var filename = "";
    var disposition = xhr.getResponseHeader('Content-Disposition');
    if (disposition && disposition.indexOf('inline') !== -1) {
        var filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
        var matches = filenameRegex.exec(disposition);
        if (matches != null && matches[1]) { 
          filename = matches[1].replace(/['"]/g, '');
        }
    }
}

More here

like image 191
Winter Soldier Avatar answered Oct 24 '22 15:10

Winter Soldier


This is an improvement on marjon4's answer.

A much simplified way to the selected answer would be to use split like this;

var fileName = xhr.getResponseHeader('content-disposition').split('filename=')[1].split(';')[0];

Note: This solution may not work as expected if your file name itself contains a semi-colon (;)

like image 35
Shivam Puri Avatar answered Oct 24 '22 13:10

Shivam Puri


Or simply just:

var fileName = xhr.getResponseHeader('Content-Disposition').split("filename=")[1];
like image 15
marjon4 Avatar answered Oct 24 '22 13:10

marjon4


If you want to get the filename and support both those weird url encoded UTF-8 headers and the ascii headers, you can use something like this

public getFileName(disposition: string): string {
    const utf8FilenameRegex = /filename\*=UTF-8''([\w%\-\.]+)(?:; ?|$)/i;
    const asciiFilenameRegex = /^filename=(["']?)(.*?[^\\])\1(?:; ?|$)/i;

    let fileName: string = null;
    if (utf8FilenameRegex.test(disposition)) {
      fileName = decodeURIComponent(utf8FilenameRegex.exec(disposition)[1]);
    } else {
      // prevent ReDos attacks by anchoring the ascii regex to string start and
      //  slicing off everything before 'filename='
      const filenameStart = disposition.toLowerCase().indexOf('filename=');
      if (filenameStart >= 0) {
        const partialDisposition = disposition.slice(filenameStart);
        const matches = asciiFilenameRegex.exec(partialDisposition );
        if (matches != null && matches[2]) {
          fileName = matches[2];
        }
      }
    }
    return fileName;
}

A couple of notes:

  1. this will take the value of the UTF-8 filename, if set, over the ascii name
  2. on download, your browser may further alter the name to replace certain characters, like ", with _ (Chrome)
  3. the ascii pattern works best for quoted file names, but supports unquoted values. In that case it treats all text after the filename= and before the either the next ; or the end of the header value as the file name.
  4. This does not clean up path information. If you are saving the file from a website, that's the browser's job, but if your using this in the context of a node app or something similar, be sure to clean up the path information per the OS and leave just the filename, or a crafted file name might be used to overwrite a system file (think of a file name like ../../../../../../../path/to/system/files/malicious.dll)

MDN Content Disposition Header

like image 15
J Scott Avatar answered Oct 24 '22 13:10

J Scott


In my case the header looks like this:

attachment; filename="test-file3.txt"

Therefore I was able to extract the filename pretty easily with a named group regexp:

const regExpFilename = /filename="(?<filename>.*)"/;

const filename: string | null = regExpFilename.exec(contentDispositionHeader)?.groups?.filename ?? null;

I know I'm slightly off topic here as OP doesn't have the quotes around the filename but still sharing in case someone comes across the same pattern as I just did

like image 13
maxime1992 Avatar answered Oct 24 '22 14:10

maxime1992