想要强制下载资源而不是直接在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 火狐 歌剧


当前回答

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草案的约束。

其他回答

经典ASP解决方案

大多数现代浏览器现在都支持将文件名作为UTF-8传递,但我使用的文件上传解决方案是基于FreeASPUpload的。Net(站点已经不存在了,链接指向archive.org),它不会工作,因为二进制解析依赖于读取单字节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中的by睾string()函数,我能够获得UTF-8文件名。


有用的链接

一个ASP经典应用程序中的Multipart/form-data和UTF-8 Unicode, UTF, ASCII, ANSI格式的差异

我们在一个web应用程序中遇到了类似的问题,最后从HTML <input type="file">中读取文件名,并在一个新的HTML <input type="hidden">中以url编码的形式设置它。当然,我们必须删除一些浏览器返回的“C:\fakepath\”这样的路径。

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

在。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

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

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

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

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