想要强制下载资源而不是直接在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);

其他回答

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

库类Unicode中的方法mimeHeaderEncode($string)可以完成这项工作。

$file_name= Unicode::mimeHeaderEncode($file_name);

drupal/php中的例子:

https://github.com/drupal/core-utility/blob/8.8.x/Unicode.php

/**
   * Encodes MIME/HTTP headers that contain incorrectly encoded characters.
   *
   * For example, Unicode::mimeHeaderEncode('tést.txt') returns
   * "=?UTF-8?B?dMOpc3QudHh0?=".
   *
   * See http://www.rfc-editor.org/rfc/rfc2047.txt for more information.
   *
   * Notes:
   * - Only encode strings that contain non-ASCII characters.
   * - We progressively cut-off a chunk with self::truncateBytes(). This ensures
   *   each chunk starts and ends on a character boundary.
   * - Using \n as the chunk separator may cause problems on some systems and
   *   may have to be changed to \r\n or \r.
   *
   * @param string $string
   *   The header to encode.
   * @param bool $shorten
   *   If TRUE, only return the first chunk of a multi-chunk encoded string.
   *
   * @return string
   *   The mime-encoded header.
   */
  public static function mimeHeaderEncode($string, $shorten = FALSE) {
    if (preg_match('/[^\x20-\x7E]/', $string)) {
      // floor((75 - strlen("=?UTF-8?B??=")) * 0.75);
      $chunk_size = 47;
      $len = strlen($string);
      $output = '';
      while ($len > 0) {
        $chunk = static::truncateBytes($string, $chunk_size);
        $output .= ' =?UTF-8?B?' . base64_encode($chunk) . "?=\n";
        if ($shorten) {
          break;
        }
        $c = strlen($chunk);
        $string = substr($string, $c);
        $len -= $c;
      }
      return trim($output);
    }
    return $string;
  }

RFC 6266描述了“超文本传输协议(HTTP)中内容处理报头字段的使用”。引用其中的话:

6. 国际化的考虑 参数" filename* "(章节4.3),使用定义的编码 在[RFC5987]中,允许服务器传输外部的字符 ISO-8859-1字符集,也可以选择指定语言 在使用。

在例子部分:

这个示例与上面的示例相同,但添加了"filename" 参数,用于与未实现的用户代理的兼容性 RFC 5987: 附加:附件; 文件名= "欧元利率”; 文件名* = utf - 8”% e2 % 82% ac % 20率 注意:不支持RFC 5987编码的用户代理 当" filename "后面出现" filename* "时,忽略" filename* "。

在附录D中,还列出了一长串提高互操作性的建议。它还指向一个比较实现的站点。适用于常用文件名的当前全通过测试包括:

attwithisofnplain:普通的ISO-8859-1文件名,双引号,不带编码。这要求文件名完全符合ISO-8859-1,并且不包含百分号,至少在十六进制数字前面不包含百分号。 Attfnboth:上述顺序的两个参数。应该适用于大多数浏览器上的大多数文件名,尽管IE8将使用" filename "参数。

RFC 5987又引用了描述实际格式的RFC 2231。2231主要用于邮件,5987告诉我们哪些部分也可以用于HTTP报头。不要将其与多部分/form-data HTTP主体中使用的MIME头相混淆,后者受RFC 2388(特别是4.4节)和HTML 5草案的约束。

在。net 4.5(和Core 1.0)中,你可以使用ContentDispositionHeaderValue来为你格式化。

var fileName = "Naïve file.txt";
var h = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");
h.FileNameStar = fileName;
h.FileName = "fallback-ascii-name.txt";

Response.Headers.Add("Content-Disposition", h.ToString());

h.ToString()将导致:

attachment; filename*=utf-8''Na%C3%AFve%20file.txt; filename=fallback-ascii-name.txt

在PHP中,这为我做了(假设文件名是UTF8编码):

header('Content-Disposition: attachment;'
    . 'filename="' . addslashes(utf8_decode($filename)) . '";'
    . 'filename*=utf-8\'\'' . rawurlencode($filename));

在IE8-11、Firefox和Chrome浏览器上进行测试。 如果浏览器可以解释文件名*=utf-8,它将使用文件名的UTF8版本,否则它将使用解码后的文件名。如果你的文件名包含的字符不能在ISO-8859-1中表示,你可能要考虑使用iconv代替。