64

May i know how to assign a file-name to a a-href file download.

<a href="http://localhost:8080/couch/getFile?dbName=xxx&file=test.xml">get-file</a>

On right-clicking and Save as: A services running on the background will return test.xml contents and the user can save it locally. But here everytime user needs to type a filename for saving. Instead i'm thinking to pull the test.xml. May i know how to tell the browser to use "test.xml" as a download file name?

Will setting headers on HTTP response would work? if so may i know how we can do that?

johnnyRose
  • 7,310
  • 17
  • 40
  • 61
Shiv
  • 1,211
  • 4
  • 14
  • 24

4 Answers4

110

You need to append the HTTP response header "Content-Disposition"

Response.AppendHeader("content-disposition", "attachment; filename=\"" + fileName +"\"");
Dharman
  • 30,962
  • 25
  • 85
  • 135
vtortola
  • 34,709
  • 29
  • 161
  • 263
  • 3
    Note that IE seems to treat the single quotes as part of the file name. – Bruno Sep 09 '13 at 20:55
  • 2
    Single quotes are not valid for the filename. See [`quoted-string`](http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html) – Markus Malkusch Aug 12 '15 at 22:22
  • 1
    I replaced the single quotes. – vtortola Aug 13 '15 at 09:18
  • 1
    If `filename` contains quotes or backslashes (allowed on UNIX), this will break, as those needs to be escaped. – Flimm Feb 03 '16 at 12:27
  • 2
    Quoted pairs (escaping) cannot be used because it breaks existing software. Instead, escape " as `%22` and keep backslash as ```\``` because that is the de-facto standard followed by all existing software. For instance, if you upload a file named ```foo"bar\```, the browser sends `filename="foo%22bar\"` (tested with Firefox and Chrome on Mac in 2019). If you try to follow the RFC and treat `\"` as an escape sequence, parsing fails. – Tronic Aug 23 '19 at 08:33
40

The HTTP header Content-Disposition allows you to suggest a file name.

The Content-Disposition response header field […] can be used to attach additional metadata, such as the filename to use when saving the response payload locally.

If you look at the BNF you'll see that the filename is specified as a quoted-string:

quoted-string = ( <"> *(qdtext | quoted-pair ) <"> )

This would be a valid example:

Content-Disposition: attachment; filename="fname.ext"

Please note that single quotes ' are not valid. If you need to include quotes (") in your filename you can use ". However RFC-6266 suggests to avoid including quotes:

Avoid including the "" character in the quoted-string form of the filename parameter, as escaping is not implemented by some user agents, and "" can be considered an illegal path character.

Community
  • 1
  • 1
Markus Malkusch
  • 7,738
  • 2
  • 38
  • 67
  • 1
    For quoted-string, how do you escape quotes in the filename? – Flimm Feb 02 '16 at 17:39
  • 2
    `quoted-pair` is "\" CHAR, whereas CHAR contains ". So \" will do that. The RFC says that also in its BNF: The backslash character ("\") MAY be used as a single-character quoting mechanism only within quoted-string – Markus Malkusch Feb 02 '16 at 20:59
  • 1
    The updated [RFC-6266](https://tools.ietf.org/html/rfc6266#appendix-D) suggests: Avoid including the "\" character in the quoted-string form of the filename parameter, as escaping is not implemented by some user agents, and "\" can be considered an illegal path character. – Markus Malkusch Sep 28 '17 at 09:45
  • 1
    If you don't want the browser to force a download, which is what I understand the OP to be requesting, then use "inline" instead of "attachment" `Content-Disposition: inline; filename="fname.ext"` – Peter Brand Nov 30 '17 at 12:42
11

In modern browsers you can also use download attribute on link tag:

<a href="http://localhost:8080/couch/getFile?dbName=xxx&file=test.xml" download="test.xml">
    get-file
</a>

You can check it's support on Can I use

jcubic
  • 61,973
  • 54
  • 229
  • 402
1

To set UTF-8 filenames for specific languages like UA, DE, RU etc.

Content-Disposition: Attachment; Filename*=UTF-8'en'an%20example

Documentation:

There is an interesting sample: https://github.com/thephpleague/csv/blob/master/src/AbstractCsv.php

protected function sendHeaders(string $filename): void
{
    if (strlen($filename) !== strcspn($filename, '\\/')) {
        throw InvalidArgument::dueToInvalidHeaderFilename($filename);
    }

    $flag = FILTER_FLAG_STRIP_LOW;
    if (strlen($filename) !== mb_strlen($filename)) {
        $flag |= FILTER_FLAG_STRIP_HIGH;
    }

    /** @var string $filtered_name */
    $filtered_name = filter_var($filename, FILTER_UNSAFE_RAW, $flag);
    $filename_fallback = str_replace('%', '', $filtered_name);

    $disposition = sprintf('attachment; filename="%s"', str_replace('"', '\\"', $filename_fallback));
    if ($filename !== $filename_fallback) {
        $disposition .= sprintf("; filename*=utf-8''%s", rawurlencode($filename));
    }

    header('Content-Type: text/csv');
    header('Content-Transfer-Encoding: binary');
    header('Content-Description: File Transfer');
    header('Content-Disposition: '.$disposition);
}
Joachim Sauer
  • 302,674
  • 57
  • 556
  • 614
Eugene Kaurov
  • 2,356
  • 28
  • 39