HTTP에서 Content-Disposition 헤더의 파일 이름 매개변수를 어떻게 인코딩합니까?

StackOverflow https://stackoverflow.com/questions/93551

문제

리소스를 강제로 사용하려는 웹 애플리케이션 다운로드됨 직접적으로보다는 렌더링됨 웹 브라우저에서 문제 Content-Disposition 다음 형식의 HTTP 응답 헤더에:

Content-Disposition: attachment; filename=파일 이름

그만큼 filename 매개변수는 브라우저가 리소스를 다운로드하는 파일의 이름을 제안하는 데 사용될 수 있습니다. RFC 2183 (Content-Disposition) 그러나 다음과 같이 명시되어 있습니다. 섹션 2.3 (파일 이름 매개변수) 파일 이름은 US-ASCII 문자만 사용할 수 있습니다.

현재 [RFC 2045] 문법은 매개 변수 값 (및 내용화 파일 이름)을 US-ASCII로 제한합니다.우리는 파일 이름에서 임의의 문자 세트를 허용하는 데 큰 바람직 함을 인식하지만, 필요한 메커니즘을 정의하는 것은이 문서의 범위를 벗어납니다.

그럼에도 불구하고 오늘날 가장 널리 사용되는 웹 브라우저는 US-ASCII가 아닌 문자를 허용하는 것 같지만(표준이 없기 때문에) 파일 이름의 인코딩 체계와 문자 집합 사양에 동의하지 않는다는 경험적 증거가 있습니다.그렇다면 파일 이름 "naïvefile"(따옴표 없이 세 번째 문자는 U+00EF)을 Content-Disposition 헤더에 인코딩해야 하는 경우 인기 있는 브라우저에서 사용하는 다양한 구성표와 인코딩은 무엇입니까?

이 질문의 목적을 위해, 인기 있는 브라우저 존재:

  • 파이어폭스
  • 인터넷 익스플로러
  • 원정 여행
  • 구글 크롬
  • 오페라
도움이 되었습니까?

해결책

제안된 문서에는 브라우저 테스트 및 이전 버전과의 호환성에 대한 링크를 포함하여 이에 대한 논의가 있습니다. RFC 5987, "HTTP(Hypertext Transfer Protocol) 헤더 필드 매개변수에 대한 문자 세트 및 언어 인코딩."

RFC 2183 해당 헤더가 다음에 따라 인코딩되어야 함을 나타냅니다. RFC 2184, 에 의해 폐기되었습니다. RFC 2231, 위의 RFC 초안에서 다룹니다.

다른 팁

나는 이것이 오래된 게시물이라는 것을 알고 있지만 여전히 관련성이 매우 높습니다.최신 브라우저는 utf-8 인코딩, 백분율 인코딩(url 인코딩)을 허용하는 rfc5987을 지원하는 것으로 나타났습니다.그러면 Naïve file.txt는 다음과 같습니다.

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

Safari(5)는 이를 지원하지 않습니다.대신 utf-8로 인코딩된 헤더에 파일 이름을 직접 작성하는 Safari 표준을 사용해야 합니다.

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

IE8 및 이전 버전도 이를 지원하지 않으며 utf-8 인코딩, 백분율 인코딩의 IE 표준을 사용해야 합니다.

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

@톰Z:IE7과 IE8에서 테스트한 결과 아포스트로피(')를 이스케이프 처리할 필요가 없는 것으로 나타났습니다.실패한 예가 있나요?

@Dave Van den Eynde:RFC6266에 따라 두 파일 이름을 한 줄에 결합하면 Android 및 IE7+8을 제외하고 작동하며 이를 반영하도록 코드를 업데이트했습니다.제안해 주셔서 감사합니다.

@틸로:GoodReader 또는 기타 비 브라우저에 대해서는 전혀 모릅니다.Android 접근 방식을 사용하면 운이 좋을 수도 있습니다.

@Alex Zhukovskiy:이유는 모르겠지만 위에서 논의한 것처럼 연결하다 별로 잘 작동하지 않는 것 같습니다.

간단하고 매우 강력한 대안이 있습니다. 원하는 파일 이름이 포함된 URL을 사용하세요..

마지막 슬래시 뒤의 이름이 원하는 이름이면 추가 헤더가 필요하지 않습니다!

이 트릭은 작동합니다:

/real_script.php/fake_filename.doc

서버가 URL 재작성을 지원하는 경우(예: mod_rewrite Apache에서는) 스크립트 부분을 완전히 숨길 수 있습니다.

URL의 문자는 바이트 단위로 urlencoded된 UTF-8이어야 합니다.

/mot%C3%B6rhead   # motörhead

RFC 6266 "를 설명합니다.HTTP(Hypertext Transfer Protocol)에서 콘텐츠 처리 헤더 필드 사용".그 내용을 인용하면 다음과 같습니다.

6.국제화 고려 사항

filename*” 매개변수(섹션 4.3), 정의 된 인코딩 사용 [RFC5987], 서버가 ISO-8859-1 문자 세트 외부에서 문자를 전송하고 사용중인 언어를 선택적으로 지정할 수 있습니다.

그리고 그들의 예제 섹션:

이 예제는 위의 예와 동일하지만 구현하지 않는 사용자 에이전트와의 호환성을위한 "Filename"매개 변수를 추가합니다. RFC 5987:

Content-Disposition: attachment;
                     filename="EURO rates";
                     filename*=utf-8''%e2%82%ac%20rates

메모:지원하지 않는 사용자 에이전트 RFC 5987 인코딩 "무시"filename*”가 “다음에 발생하는 경우filename”.

~ 안에 부록 D 상호 운용성을 높이기 위한 많은 제안 목록도 있습니다.그것은 또한 다음을 가리킨다. 구현을 비교하는 사이트.일반적인 파일 이름에 적합한 현재 전체 통과 테스트는 다음과 같습니다.

  • attwithisofnplain:큰따옴표가 있고 인코딩이 없는 일반 ISO-8859-1 파일 이름입니다.여기에는 모두 ISO-8859-1이고 적어도 16진수 앞에 백분율 기호가 포함되지 않은 파일 이름이 필요합니다.
  • 둘 다 attfnn:위에서 설명한 순서대로 두 개의 매개변수를 지정합니다.대부분의 브라우저에서 대부분의 파일 이름에 대해 작동해야 하지만 IE8에서는 "filename” 매개변수입니다.

저것 RFC 5987 차례로 참조 RFC 2231, 실제 형식을 설명합니다.2231은 주로 메일용이고 5987은 HTTP 헤더에도 사용할 수 있는 부분을 알려줍니다.이것을 내부에서 사용되는 MIME 헤더와 혼동하지 마십시오. multipart/form-data HTTP , 이는 다음에 의해 관리됩니다. RFC 2388 (섹션 4.4 특히) 그리고 HTML 5 초안.

다음 문서는 다음에서 링크되었습니다. RFC 초안 언급된 그의 답변에서 질문에 대해 더 자세히 설명하고 여기에 직접 언급할 가치가 있습니다.

HTTP Content-Disposition 헤더 및 RFC 2231/2047 인코딩에 대한 테스트 사례

asp.net mvc2에서는 다음과 같은 것을 사용합니다.

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

mvc(2)를 사용하지 않는다면 다음을 사용하여 파일 이름을 인코딩할 수 있을 것 같습니다.

HttpUtility.UrlPathEncode(fileName)

파일 이름을 큰따옴표로 묶어주세요.나를 위해 문제를 해결했습니다.이와 같이:

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

http://kb.mozillazine.org/Filenames_with_spaces_are_truncated_upon_download

여러 옵션을 테스트했습니다.브라우저는 사양을 지원하지 않고 다르게 작동하므로 큰따옴표가 최선의 선택이라고 생각합니다.

인코딩을 위해 다음 코드 조각을 사용합니다(가정 파일 이름 파일 이름과 파일 확장자를 포함합니다. 예:테스트.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 + "\"");

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

IE 9 Not fixed
IE 9 Fixed

이전 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.'"');

nodejs 백엔드를 사용하는 경우 내가 찾은 다음 코드를 사용할 수 있습니다. 여기

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

나는 "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 및 "안전한" 문자만 사용하는 한 filename="..."의 표준 방식을 사용합니다.그렇지 않은 경우 filename*=UTF-8'' URL 인코딩 방식을 추가합니다.에 따르면 이 특정 테스트 사례, MSIE9 이상 및 최신 FF, Chrome, Safari에서 작동합니다.낮은 MSIE 버전에서는 파일 이름의 ISO8859-1 버전을 포함하는 파일 이름을 제공해야 하며 이 인코딩에 없는 문자에는 밑줄이 표시되어야 합니다.

최종 참고사항:최대.각 헤더 필드의 크기는 Apache에서 8190바이트입니다.UTF-8은 문자당 최대 4바이트일 수 있습니다.rawurlencode 이후에는 문자당 x3 = 12바이트입니다.꽤 비효율적이지만 이론적으로는 파일 이름에 600개 이상의 "웃음" %F0%9F%98%81을 포함하는 것이 가능합니다.

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 대신에.

클래식 ASP 솔루션

대부분의 최신 브라우저는 Filename ~처럼 UTF-8 지금은 내가 사용하는 파일 업로드 솔루션의 경우와 마찬가지로 다음을 기반으로 했습니다. FreeASPUpload.Net (사이트가 더 이상 존재하지 않습니다. 링크는 다음을 가리킵니다. archive.org) 단일 바이트 ASCII 인코딩 문자열 읽기에 의존하는 바이너리 구문 분석은 작동하지 않습니다. 이는 ASCII가 지원하지 않는 문자에 도달할 때까지 UTF-8 인코딩 데이터를 전달할 때 제대로 작동했습니다.

그러나 바이너리를 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

신용은 다음과 같습니다 순수 ASP 파일 업로드 구현함으로써 BytesToString() 기능 include_aspuploader.asp 내 자신의 코드에서 나는 얻을 수 있었다 UTF-8 파일 이름이 작동 중입니다.


유용한 링크

오늘 고객 문제에 대한 응답으로 이 모든 것을 시도한 이후의 업데이트입니다.

  • 일본어용으로 구성된 Safari를 제외하고 고객이 테스트한 모든 브라우저는 filename=text.pdf에서 가장 잘 작동했습니다. 여기서 text는 URL 인코딩 없이 utf-8로 ASP.Net/IIS에 의해 직렬화된 고객 값입니다.어떤 이유로 영어로 구성된 Safari는 utf-8 일본어 이름을 가진 파일을 허용하고 올바르게 저장하지만 일본어로 구성된 동일한 브라우저는 해석되지 않은 utf-8 문자로 파일을 저장합니다.테스트된 다른 모든 브라우저는 URL 인코딩 없이 utf-8로 인코딩된 파일 이름을 사용하여 (언어 구성에 관계없이) 가장 잘 작동하는 것 같았습니다.
  • Rfc5987/8187을 구현하는 단일 브라우저를 찾을 수 없습니다. 조금도.저는 최신 Chrome, Firefox 빌드, IE 11 및 Edge를 사용하여 테스트했습니다.filename*=utf-8''texturlencoded.pdf로 헤더를 설정해 보았습니다. filename=text.pdf;파일 이름*=utf-8''texturlencoded.pdf.Rfc5987/8187의 어떤 기능도 위의 어느 것에서도 올바르게 처리되지 않는 것으로 나타났습니다.

웹 애플리케이션에서도 비슷한 문제가 있었고 결국 HTML에서 파일 이름을 읽게 되었습니다. <input type="file">, 새 HTML의 URL 인코딩 형식으로 설정 <input type="hidden">.물론 일부 브라우저에서 반환되는 "C:\fakepath\"와 같은 경로를 제거해야 했습니다.

물론 이것은 OP 질문에 직접적으로 대답하지는 않지만 다른 사람들에게는 해결책이 될 수 있습니다.

나는 일반적으로 파일 이름을 URL 인코딩(%xx 사용)하는데 모든 브라우저에서 작동하는 것 같습니다.어쨌든 몇 가지 테스트를 수행하고 싶을 수도 있습니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top