ChatGPT解决这个技术问题 Extra ChatGPT

如何在 HTTP 中对 Content-Disposition 标头的文件名参数进行编码?

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

Content-Disposition: attachment; filename=FILENAME

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

当前 [RFC 2045] 语法将参数值(以及因此的 Content-Disposition 文件名)限制为 US-ASCII。我们认识到允许在文件名中使用任意字符集是非常可取的,但是定义必要的机制超出了本文档的范围。

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

对于这个问题,流行的浏览器是:

谷歌浏览器

苹果浏览器

Internet Explorer 或边缘

火狐

歌剧

让它适用于 Mobile Safari(@Martin Ørding-Thomsen 建议的原始 utf-8),但这不适用于同一设备上的 GoodReader。有任何想法吗?
Kornel's answer 证明是阻力最小的路径,如果您可以设置路径的最后一段;将此与 Content-Disposition: attachment 结合起来。

M
Martin Ørding-Thomsen

我知道这是一个旧帖子,但它仍然非常相关。我发现现代浏览器支持 rfc5987,它允许 utf-8 编码、百分比编码(url 编码)。然后 Naïve file.txt 变为:

Content-Disposition: attachment; filename*=UTF-8''Na%C3%AFve%20file.txt

Safari (5) 不支持此功能。相反,您应该使用 Safari 标准将文件名直接写入 utf-8 编码的标头中:

Content-Disposition: attachment; filename=Naïve file.txt

IE8 及更早版本也不支持,需要使用 IE 标准的 utf-8 编码,百分比编码:

Content-Disposition: attachment; filename=Na%C3%AFve%20file.txt

在 ASP.Net 中,我使用以下代码:

string contentDisposition;
if (Request.Browser.Browser == "IE" && (Request.Browser.Version == "7.0" || Request.Browser.Version == "8.0"))
    contentDisposition = "attachment; filename=" + Uri.EscapeDataString(fileName);
else if (Request.Browser.Browser == "Safari")
    contentDisposition = "attachment; filename=" + fileName;
else
    contentDisposition = "attachment; filename*=UTF-8''" + Uri.EscapeDataString(fileName);
Response.AddHeader("Content-Disposition", contentDisposition);

我使用 IE7、IE8、IE9、Chrome 13、Opera 11、FF5、Safari 5 测试了上述内容。

2013 年 11 月更新:

这是我目前使用的代码。我仍然要支持IE8,所以我无法摆脱第一部分。事实证明,Android 上的浏览器使用内置的 Android 下载管理器,它无法以标准方式可靠地解析文件名。

string contentDisposition;
if (Request.Browser.Browser == "IE" && (Request.Browser.Version == "7.0" || Request.Browser.Version == "8.0"))
    contentDisposition = "attachment; filename=" + Uri.EscapeDataString(fileName);
else if (Request.UserAgent != null && Request.UserAgent.ToLowerInvariant().Contains("android")) // android built-in download manager (all browsers on android)
    contentDisposition = "attachment; filename=\"" + MakeAndroidSafeFileName(fileName) + "\"";
else
    contentDisposition = "attachment; filename=\"" + fileName + "\"; filename*=UTF-8''" + Uri.EscapeDataString(fileName);
Response.AddHeader("Content-Disposition", contentDisposition);

以上内容现已在 IE7-11、Chrome 32、Opera 12、FF25、Safari 6 中测试,使用此文件名进行下载:你好abcABCæøåÆØÅäöüïëêîâéíáóúýñ½§!#¤%&()=`@£$€{[]}+´¨ ^~'-_,;.txt

在 IE7 上,它适用于某些字符,但不是全部。但是现在谁在乎IE7?

这是我用来为 Android 生成安全文件名的函数。请注意,我不知道 Android 支持哪些字符,但我已经测试过这些字符确实有效:

private static readonly Dictionary<char, char> AndroidAllowedChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ._-+,@£$€!½§~'=()[]{}0123456789".ToDictionary(c => c);
private string MakeAndroidSafeFileName(string fileName)
{
    char[] newFileName = fileName.ToCharArray();
    for (int i = 0; i < newFileName.Length; i++)
    {
        if (!AndroidAllowedChars.ContainsKey(newFileName[i]))
            newFileName[i] = '_';
    }
    return new string(newFileName);
}

@TomZ:我在 IE7 和 IE8 中进行了测试,结果证明我不需要转义撇号 (')。你有一个失败的例子吗?

@Dave Van den Eynde:根据 RFC6266 将两个文件名组合在一行上,Android 和 IE7+8 除外,我已经更新了代码以反映这一点。感谢您的建议。

@Thilo:不知道 GoodReader 或任何其他非浏览器。使用 Android 方法可能会有一些运气。

@Alex Zhukovskiy:我不知道为什么,但正如在 Connect 上讨论的那样,它似乎不太好用。


让它适用于 Mobile Safari(如上所述的原始 utf-8),但这不适用于同一设备上的 GoodReader。有任何想法吗?
IE7 和 8 也需要转义撇号:.Replace("'", Uri.HexEscape('\''))
直接编写 UTF-8 字符似乎适用于当前版本的 Firefox、Chrome 和 Opera。没有测试 Safari 和 IE。
为什么不将它们组合为 Content-Disposition: attachment; filename*=UTF-8''Na%C3%AFve%20file.txt; filename=Na%C3%AFve%20file.txt 并跳过浏览器嗅探?那行得通吗?
fastmail 的好心人找到了另一种解决方法:blog.fastmail.com/2011/06/24/download-non-english-filenames Content-Disposition:附件;文件名="foo-%c3%a4.html"; filename*=UTF-8''foo-%c3%a4.html 指定 fileName 两次(一次没有 UTF-8 前缀,一次有)使其在 IE8-11、Edge、Chrome、Firefox 和 Safari 中工作(看起来像苹果固定的野生动物园,所以它现在也可以在那里工作)
K
Kornel

在 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,按字节进行 urlencoded:

/mot%C3%B6rhead   # motörhead

尝试 GetAttachment.aspx/fake_filename.doc?id=34 (尽管它可能是 Apache 独有的怪癖)
我沿着兔子之路走下去,尝试了其他一些解决方案;试图找出正确的浏览器和版本以正确设置标题是一场噩梦。 Chrome 被错误地识别为 Safari,它的行为根本不一样(如果编码不正确,则以逗号分隔)。省去麻烦,使用此解决方案并根据需要为 URL 设置别名。
/:id/:filename 方法非常简单有效,谢谢!
一千次“是”。你会认真地赢得时间。甚至 - 一些 Android 浏览器会完全忽略 Content-Disposition 并创建非常有趣的文件名(它们将从您的路径生成)。因此,保持理智的唯一解决方案就是设置 Content-Disposition: attachment 并将所需的文件名作为最后一个路径组件传递:
在相关说明中,这是一个很好的解决方案(让我觉得有点愚蠢),请记住,如果文件名来自用户变量,您仍然必须确保它已准备好用于文件系统。如果您不这样做,并且该文件有类似 / 的内容,您会得到真的 奇怪的浏览器错误。以 this answer 作为参考,我使用了 s.replace(/[\000-\031\\\/:*?"<>\|]/g, '_')
C
Community

在建议的 RFC 5987“超文本传输协议 (HTTP) 标头字段参数的字符集和语言编码”中对此进行了讨论,包括浏览器测试和向后兼容性的链接。

RFC 2183 表示此类标头应根据 RFC 2184 进行编码,该 RFC 2184 已被 RFC 2231 废弃,包含在上面的 RFC 草案中。


另请注意,互联网草案(不是“草案 RFC”)已经完成,最终文档为 RFC 5987 (greenbytes.de/tech/webdav/rfc5987.html)
与此相关,我发现如果文件名中有逗号 (,),例如 Content-Disposition: filename="foo, bar.pdf",Firefox(包括版本 4-9)会中断。结果是 Firefox 正确下载了文件,但保留了 .part 扩展名(例如 foo,bar.pdf-1.part)。然后,当然该文件将无法正确打开,因为该应用程序未与 .part 关联。其他 ASCII 字符似乎可以正常工作。
有关 IE 行为的详细信息,请参阅blogs.msdn.com/b/ieinternals/archive/2010/06/07/…
@catchdave:您忘记了“附件;”部分。
C
Community

RFC 6266 描述了“超文本传输协议 (HTTP) 中 Content-Disposition 标头字段的使用”。从中引用:

6. 国际化注意事项 “filename*”参数(第 4.3 节)使用 [RFC5987] 中定义的编码,允许服务器传输 ISO-8859-1 字符集之外的字符,并且还可以选择指定使用的语言。

在他们的 examples section 中:

此示例与上面的示例相同,但添加了“文件名”参数以与未实现 RFC 5987 的用户代理兼容:Content-Disposition: attachment; filename="欧元汇率"; filename*=utf-8''%e2%82%ac%20rates 注意:那些不支持 RFC 5987 编码的用户代理会忽略出现在“filename”之后的“filename*”。

Appendix D 中还有一长串提高互操作性的建议。它还指向 a site which compares implementations。当前适用于常见文件名的全通测试包括:

attwithisofnplain:带有双引号且不带编码的纯 ISO-8859-1 文件名。这需要一个全部为 ISO-8859-1 且不包含百分号的文件名,至少不包含在十六进制数字前面。

attfnboth:按上述顺序的两个参数。应该适用于大多数浏览器上的大多数文件名,尽管 IE8 将使用“文件名”参数。

RFC 5987 又引用 RFC 2231,它描述了实际格式。 2231 主要用于邮件,5987 告诉我们哪些部分也可用于 HTTP 标头。不要将此与 multipart/form-data HTTP body 中使用的 MIME 标头混淆,后者由 RFC 2388(特别是 section 4.4)和 HTML 5 draft 管理。


我在 Safari 中遇到了麻烦。下载带有俄语名称的文件时,会收到错误且不可读的字符。该解决方案有所帮助。但是我们需要在一行中发送一个标题(!!!)。
C
Community

Jim 在他的回答中提到的从 the draft RFC 链接的以下文档进一步解决了这个问题,在这里绝对值得直接注意:

Test Cases for HTTP Content-Disposition header and RFC 2231/2047 Encoding


请注意,可以提供两种编码文件名参数的方法,并且它们似乎可以在旧浏览器和新浏览器中正常工作(在这种情况下,旧浏览器是 MSIE8 和 Safari)。检查@AtifAziz 提到的报告中的attfnboth
K
Kornel

我使用以下代码片段进行编码(假设 fileName 包含文件的文件名和扩展名,即:test.txt):

PHP:

if ( strpos ( $_SERVER [ 'HTTP_USER_AGENT' ], "MSIE" ) > 0 )
{
     header ( 'Content-Disposition: attachment; filename="' . rawurlencode ( $fileName ) . '"' );
}
else
{
     header( 'Content-Disposition: attachment; filename*=UTF-8\'\'' . rawurlencode ( $fileName ) );
}

爪哇:

fileName = request.getHeader ( "user-agent" ).contains ( "MSIE" ) ? URLEncoder.encode ( fileName, "utf-8") : MimeUtility.encodeWord ( fileName );
response.setHeader ( "Content-disposition", "attachment; filename=\"" + fileName + "\"");

对,至少对于 filename*= 处置标头,它在 PHP 中应该是 rawurlencode,因为 RFC 6266->RFC 5987 的 ext-value 中使用的 value-chars(请参阅 tools.ietf.org/html/rfc6266#section-4.1 & tools.ietf.org/html/rfc5987#section-3.2.1 )没有允许没有百分比转义的空间(另一方面,filename= 似乎可以允许一个没有转义的空间,尽管这里应该只存在 ASCII)。没有必要使用 rawurlencode 的完全严格性进行编码,因此可以对一些字符进行非转义:gist.github.com/brettz9/8752120
D
Dmitry Kaigorodov

将文件名放在双引号中。为我解决了这个问题。像这样:

Content-Disposition: attachment; filename="My Report.doc"

http://kb.mozillazine.org/Filenames_with_spaces_are_truncated_upon_download

我已经测试了多个选项。浏览器不支持规范并且行为不同,我相信双引号是最好的选择。


遗憾的是,这并不能解决上述答案中解释的所有问题。
这将允许您返回带有空格、&%# 等的文件名。所以它解决了这个问题。
如果文件名包含双引号怎么办(是的,这可能发生),如 RFC 6266 中所述,文件名是“带引号的字符串”,并且如 RFC 2616 中所述,带引号的字符串中的双引号应使用反斜杠进行转义。
@ChristopheRoussy 有没有办法允许在文件名中使用双引号?我尝试了一堆用单引号括起来的组合,转义双引号(\")等,但它从来没有奏效。最终我不得不使用 gsub 来删除双引号。因此,如果 filenameMy 2" Report.doc,它最终会是 My 2 Report.doc。不理想,但至少它有效。想法?
@JoshuaPinter 考虑转义或转义,有时您必须将字符加倍。它必须在标准中定义。关闭:stackoverflow.com/questions/18634337/…
T
Tim Cooper

在 asp.net mvc2 我使用这样的东西:

return File(
    tempFile
    , "application/octet-stream"
    , HttpUtility.UrlPathEncode(fileName)
    );

我想如果你不使用 mvc(2) 你可以只使用编码文件名

HttpUtility.UrlPathEncode(fileName)

文件名编码的 URL 编码无效,浏览器不应该对它们进行 url 解码。
IE 11 绝对不会解码此字段中的 url 编码。
但是当浏览器是 Chrome 或 IE 时需要进行 UrlEncoded,其他如 FF、Safari 和 Opera 可以在没有编码的情况下正常工作
m
martinoss

在 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;
    }
}

https://i.stack.imgur.com/Fsla4.jpg


G
Gustav

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

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

针对 IE8-11、Firefox 和 Chrome 进行了测试。
如果浏览器可以解释 filename*=utf-8,它将使用 UTF8 版本的文件名,否则它将使用解码的文件名。如果您的文件名包含 ISO-8859-1 中无法表示的字符,您可能需要考虑改用 iconv


尽管此代码可能会回答问题,但提供有关 为什么 和/或 如何 回答问题的附加上下文将显着提高其长期价值。请edit您的回答以添加一些解释。
哇,以上仅代码的答案都没有像那样被否决或批评。我还发现为什么已经得到了很好的回答:IE 不解释文件名 *=utf-8 但需要文件名的 ISO8859-1 版本,该脚本确实提供了该文件名。只想给懒人一个工作简单的 PHP 代码。
我认为这被否决了,因为问题不是特定于语言的,而是关于在实现标头编码时要坚持的 RFC。然而,感谢这个答案,对于 PHP,这段代码让我的烦恼消失了。
谢谢你。这个答案可能没有严格回答这个问题,但它正是我正在寻找的,并帮助我解决了 Python 中的问题。
如果用户可以控制文件的名称,我很确定这段代码可以用作攻击媒介。
E
Emanuele Spatola

如果您使用的是 nodejs 后端,则可以使用我找到的以下代码 here

var fileName = 'my file(2).txt';
var header = "Content-Disposition: attachment; filename*=UTF-8''" 
             + encodeRFC5987ValueChars(fileName);

function encodeRFC5987ValueChars (str) {
    return encodeURIComponent(str).
        // Note that although RFC3986 reserves "!", RFC5987 does not,
        // so we do not need to escape it
        replace(/['()]/g, escape). // i.e., %27 %28 %29
        replace(/\*/g, '%2A').
            // The following are not required for percent-encoding per RFC5987, 
            // so we can allow for a little better readability over the wire: |`^
            replace(/%(?:7C|60|5E)/g, unescape);
}

最好使用 encodeURI(str)。以文件名中的日期为例:encodeURIComponent('"Kornél Kovács 1/1/2016') => "Kornél Kovács 1%2F1%2F2016" vs. encodeURI('"Kornél Kovács 1/1/2016') => “科尔内尔·科瓦奇 2016 年 1 月 1 日”
这在 Safari 中有效吗?
S
Stano

我在所有主流浏览器中测试了以下代码,包括旧版 Explorer(通过兼容模式),它在任何地方都运行良好:

$filename = $_GET['file']; //this string from $_GET is already decoded
if (strstr($_SERVER['HTTP_USER_AGENT'],"MSIE"))
  $filename = rawurlencode($filename);
header('Content-Disposition: attachment; filename="'.$filename.'"');

请注意,这个答案来自 2012 年
a
apurkrt

我最终在“download.php”脚本中使用了以下代码(基于 this blogpostthese test cases)。

$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 和“安全”字符,这将使用标准的 filename="..." 方式;如果没有,它会添加 filename*=UTF-8'' url-encoded 方式。根据this specific test case,它应该从 MSIE9 开始工作,并且在最近的 FF、Chrome、Safari 上工作;在较低的 MSIE 版本上,它应该提供包含 ISO8859-1 版本的文件名的文件名,并在不采用这种编码的字符上带有下划线。

最后说明:最大。在 apache 上,每个标头字段的大小为 8190 字节。 UTF-8 每个字符最多可以有四个字节;在 rawurlencode 之后,每个字符 x3 = 12 个字节。效率很低,但理论上应该仍然可以在文件名中包含超过 600 个“微笑”%F0%9F%98%81。


...但最大可传输文件名长度也取决于客户端。刚刚发现最多 [89 个微笑😁].pdf 文件名通过 MSIE11。在 Firefox37 中,最多为 [111x 😁].pdf。 Chrome41 在第 110 个微笑处截断文件名。有趣的是,后缀转移好了。
u
user1664043

只是一个更新,因为我今天尝试所有这些东西以响应客户问题

除了为日语配置的 Safari 之外,我们客户测试的所有浏览器在使用 filename=text.pdf 时效果最佳 - 其中 text 是由 ASP.Net/IIS 在 utf-8 中序列化的客户值,没有 url 编码。出于某种原因,配置为英文的 Safari 会接受并正确保存具有 utf-8 日文名称的文件,但配置为日文的同一浏览器会使用未解释的 utf-8 字符保存文件。测试的所有其他浏览器似乎在文件名 utf-8 编码但没有 url 编码的情况下工作得最好/很好(无论语言配置如何)。

我根本找不到一个实现 Rfc5987/8187 的浏览器。我使用最新的 Chrome、Firefox 构建以及 IE 11 和 Edge 进行了测试。我尝试仅使用 filename*=utf-8''texturlencoded.pdf 设置标题,同时使用 filename=text.pdf 设置它;文件名*=utf-8''texturlencoded.pdf。 Rfc5987/8187 的任何一项功能似乎都没有在上述任何一项中得到正确处理。


这是一个很好的更新。您能否详细说明您尝试过的具体测试?
B
Bjarke Pjedsted

从 .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

我将它与 stackoverflow.com/a/56797567/4078503 中的“ASCII 折叠”结合起来生成 h.FileName 注意:h.FileName 不能包含引号字符(来自 ContentDispositionHeaderValue 来源:“只允许使用边界引号”)
l
luchaninov

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);

C
Community

经典的 ASP 解决方案

大多数现代浏览器现在都支持将 Filename 作为 UTF-8 传递,但就像我使用的基于 FreeASPUpload.Net 的文件上传解决方案一样(站点不再存在,链接指向 archive.org)< /em> 它不起作用,因为二进制文件的解析依赖于读取单字节 ASCII 编码的字符串,当你传递 UTF-8 编码的数据直到你得到 ASCII 不支持的字符时,它工作得很好。

但是,我能够找到一种解决方案来获取将二进制文件读取和解析为 UTF-8 的代码。

Public Function BytesToString(bytes)    'UTF-8..
  Dim bslen
  Dim i, k , N 
  Dim b , count 
  Dim str

  bslen = LenB(bytes)
  str=""

  i = 0
  Do While i < bslen
    b = AscB(MidB(bytes,i+1,1))

    If (b And &HFC) = &HFC Then
      count = 6
      N = b And &H1
    ElseIf (b And &HF8) = &HF8 Then
      count = 5
      N = b And &H3
    ElseIf (b And &HF0) = &HF0 Then
      count = 4
      N = b And &H7
    ElseIf (b And &HE0) = &HE0 Then
      count = 3
      N = b And &HF
    ElseIf (b And &HC0) = &HC0 Then
      count = 2
      N = b And &H1F
    Else
      count = 1
      str = str & Chr(b)
    End If

    If i + count - 1 > bslen Then
      str = str&"?"
      Exit Do
    End If

    If count>1 then
      For k = 1 To count - 1
        b = AscB(MidB(bytes,i+k+1,1))
        N = N * &H40 + (b And &H3F)
      Next
      str = str & ChrW(N)
    End If
    i = i + count
  Loop

  BytesToString = str
End Function

通过在我自己的代码中实现 include_aspuploader.asp 中的 BytesToString() 函数,我能够使 UTF-8 文件名正常工作,这归功于 Pure ASP File Upload

有用的链接

ASP Classic 应用程序中的 Multipart/form-data 和 UTF-8

Unicode、UTF、ASCII、ANSI 格式差异


l
laurent

对于那些需要 JavaScript 编码标头的方法的人,我发现这个函数运行良好:

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

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


M
Matoeil

库类 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;
  }

V
V G

我们在 Web 应用程序中遇到了类似的问题,最终通过从 HTML <input type="file"> 中读取文件名,并将其设置为新 HTML <input type="hidden"> 中的 url 编码形式。当然,我们必须删除某些浏览器返回的路径,例如“C:\fakepath\”。

当然,这并不能直接回答 OP 的问题,但可能是其他人的解决方案。


完全不同的问题。问题是关于下载的,你的回答是关于上传的。
D
Dario Solera

我通常对文件名进行 URL 编码(使用 %xx),它似乎适用于所有浏览器。无论如何,您可能都想做一些测试。


我确实进行了一些测试,但它在所有浏览器中都无法正常工作,因此是个问题。 :)