想要强制下载资源而不是直接在Web浏览器中呈现资源的Web应用程序在表单的HTTP响应中发出Content-Disposition报头:

Content-Disposition:附件;filename = filename

filename参数可用于建议浏览器将资源下载到的文件的名称。然而,RFC 2183 (Content-Disposition)在2.3节(文件名参数)中规定文件名只能使用US-ASCII字符:

当前[RFC 2045]语法限制 参数值(因此 内容-处置文件名)到 us - ascii。我们认可伟大的 允许任意的可取性 文件名中的字符集,但它是 超出了本文档的范围 定义必要的机制。

然而,有经验证据表明,目前大多数流行的Web浏览器似乎允许非us - ascii字符,但(由于缺乏标准)在文件名的编码方案和字符集规范上存在分歧。问题是,如果文件名“naïvefile”(不带引号,第三个字母是U+00EF)需要编码到Content-Disposition报头中,那么流行的浏览器采用了哪些不同的方案和编码?

为了解决这个问题,流行的浏览器是:

谷歌Chrome Safari Internet Explorer或Edge 火狐 歌剧


当前回答

PHP框架Symfony 4在HeaderUtils::makeDisposition中有$filenameFallback。 您可以查看这个函数的详细信息-它与上面的答案类似。

使用的例子:

$filenameFallback = preg_replace('#^.*\.#', md5($filename) . '.', $filename);
$disposition = $response->headers->makeDisposition(ResponseHeaderBag::DISPOSITION_ATTACHMENT, $filename, $filenameFallback);
$response->headers->set('Content-Disposition', $disposition);

其他回答

在ASP。NET Web API,我url编码的文件名:

public static class HttpRequestMessageExtensions
{
    public static HttpResponseMessage CreateFileResponse(this HttpRequestMessage request, byte[] data, string filename, string mediaType)
    {
        HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);
        var stream = new MemoryStream(data);
        stream.Position = 0;

        response.Content = new StreamContent(stream);

        response.Content.Headers.ContentType = 
            new MediaTypeHeaderValue(mediaType);

        // URL-Encode filename
        // Fixes behavior in IE, that filenames with non US-ASCII characters
        // stay correct (not "_utf-8_.......=_=").
        var encodedFilename = HttpUtility.UrlEncode(filename, Encoding.UTF8);

        response.Content.Headers.ContentDisposition =
            new ContentDispositionHeaderValue("attachment") { FileName = encodedFilename };
        return response;
    }
}


我通常对文件名进行url编码(使用%xx),它似乎在所有浏览器中都可以工作。你还是得做些检查。

我最终在“download.php”脚本中编写了以下代码(基于这篇博文和这些测试用例)。

$il1_filename = utf8_decode($filename);
$to_underscore = "\"\\#*;:|<>/?";
$safe_filename = strtr($il1_filename, $to_underscore, str_repeat("_", strlen($to_underscore)));

header("Content-Disposition: attachment; filename=\"$safe_filename\""
.( $safe_filename === $filename ? "" : "; filename*=UTF-8''".rawurlencode($filename) ));

只要只使用iso-latin1和“safe”字符,就使用标准的filename="…";如果不是,它会添加文件名*=UTF-8 " url编码的方式。根据这个具体的测试用例,它应该从MSIE9起,并在最近的FF, Chrome, Safari;在较低的MSIE版本中,它应该提供包含ISO8859-1版本的文件名,在非此编码的字符上使用下划线。

最后注意:最大值。在apache上,每个报头字段的大小为8190字节。UTF-8每个字符最多可以有四个字节;在rawurlencode之后,每个字符是x3 = 12字节。非常低效,但理论上仍然可以在文件名中有超过600个“smiles”%F0%9F%98%81。

对于那些需要JavaScript方式编码头的人,我发现这个函数工作得很好:

function createContentDispositionHeader(filename:string) {
    const encoded = encodeURIComponent(filename);
    return `attachment; filename*=UTF-8''${encoded}; filename="${encoded}"`;
}

这是基于Nextcloud在下载文件时的操作。文件名首先以UTF-8编码的形式出现,并且可能为了与某些浏览器兼容,文件名也不带UTF-8前缀。

在Content-Disposition中没有可互操作的方法来编码非ascii名称。浏览器兼容性是一团糟。 在Content-Disposition中使用UTF-8的理论上正确的语法是非常奇怪的:filename*=UTF-8 " foo%c3%a4(是的,这是一个星号,没有引号,除了中间的一个空单引号) 这个报头有点不太标准(HTTP/1.1规范承认它的存在,但不要求客户端支持它)。

有一种简单而可靠的替代方法:使用包含所需文件名的URL。

当最后一个斜杠后面的名称是您想要的名称时,您不需要任何额外的头文件!

这个技巧很管用:

/real_script.php/fake_filename.doc

如果你的服务器支持URL重写(例如Apache中的mod_rewrite),那么你可以完全隐藏脚本部分。

url中的字符应该是UTF-8,逐字节url编码:

/mot%C3%B6rhead   # motörhead