Pergunta

aplicativos da Web que deseja forçar um recurso para ser baixado em vez de diretamente prestados em um problema com o navegador Web um cabeçalho Content-Disposition na resposta HTTP com o formato:

Content-Disposition: attachment; filename=FILENAME

O parâmetro filename pode ser usada para sugerir um nome para o arquivo no qual o recurso é baixado pelo navegador. RFC 2183 (Content-Disposition), no entanto, os estados em seção 2.3 (o nome do arquivo de parâmetros) que o nome do arquivo só pode usar caracteres US-ASCII:

Current [RFC 2045] gramática restringe os valores dos parâmetros (e, portanto, nomes de arquivos de conteúdo-Disposição) para US-ASCII. Reconhecemos o grande desejabilidade de permitir arbitrária conjuntos de caracteres em nomes de arquivos, mas é além do escopo deste documento para definir os mecanismos necessários.

Existem evidências empíricas, no entanto, que a maioria dos navegadores da Web populares hoje parecem permitir caracteres não-US-ASCII ainda (para a falta de um padrão) discordam sobre a especificação esquema de codificação e conjunto de caracteres do nome do arquivo. Questão é, então, quais são os vários esquemas e codificações empregados pelos browsers mais populares, se o nome do arquivo “naïvefile” (sem aspas e onde a terceira carta é U + 00EF) necessários para ser codificado no cabeçalho Content-Disposition?

Para efeitos desta questão, navegadores populares sendo:

  • Firefox
  • Internet Explorer
  • Safari
  • Google Chrome
  • Opera
Foi útil?

Solução

Não há discussão sobre isso, incluindo links para testes de navegador e compatibilidade com versões anteriores, na proposta RFC 5987 , "conjunto de caracteres e codificação de idiomas para o Hypertext Transfer Protocol (HTTP) cabeçalho campo parâmetros."

RFC 2183 indica que tais cabeçalhos deve ser codificado de acordo com a RFC 2184 , que foi tornado obsoleto por RFC 2231 , abrangidos pelo projecto RFC acima.

Outras dicas

Eu sei que este é um post antigo, mas ainda é muito relevante. Eu descobri que os navegadores modernos suportam rfc5987, que permite a codificação UTF-8, percentual codificado (url-codificado). Então file.txt Naïve torna-se:

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

Safari (5) não suporta isso. Em vez disso você deve usar o padrão Safari de escrever o nome do arquivo diretamente no seu utf-8 cabeçalho codificado:

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

IE8 e mais velhos não suportam ele quer e você precisa usar o padrão IE de codificação UTF-8, percentual codificado:

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

Em ASP.Net Eu uso o seguinte código:

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

I testado o anterior utilizando IE7, IE8, IE9, Chrome 13, Opera 11, FF5, Safari 5.

Atualizar Novembro de 2013:

Aqui está o código que eu uso atualmente. Eu ainda tenho que apoiar IE8, então eu não pode se livrar da primeira parte. Acontece que navegadores no Android usar o construída em gerenciador de download Android e pode nomes de arquivo não confiável de análise na forma padrão.

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

O acima agora testado em IE7-11, Chrome 32, Opera 12, FF25, Safari 6, usando este nome de arquivo para download:! ?? abcABCæøåÆØÅäöüïëêîâéíáóúýñ½§ # ¤% & () = '@ £ $ € {[]} + '^ ~' -_,;. txt

No IE7 ele funciona para alguns personagens, mas não todos. Mas quem se preocupa com o IE7 hoje em dia?

Esta é a função uso I para gerar nomes de arquivos seguros para Android. Note que eu não sei quais personagens são suportados em Android, mas que eu testei que estes trabalhos certa:

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: I testado no IE7 e IE8 e descobriu-se que eu não tinha necessidade de escapar apóstrofo ( '). Você tem um exemplo onde ele falhar?

@ Dave Van den Eynde: Combinando os dois nomes de arquivos em uma linha como de acordo com RFC6266 funciona, exceto para Android e IE7 + 8 e eu tenho atualizado o código para refletir isso. Obrigado pela sugestão.

@Thilo: Nenhuma idéia sobre GoodReader ou qualquer outro não-browser. Você pode ter um pouco de sorte, usando a abordagem Android.

@Alex Zhukovskiy: Não sei porquê, mas como discutido em Conectar não parece ao trabalho terrivelmente bem.

Não é um simples e alternativa muito robusto: use uma URL que contém o nome do arquivo que você deseja

.

Quando o nome após a última barra é o que você quer, você não precisa de nenhum cabeçalhos extra!

Esse truque funciona:

/real_script.php/fake_filename.doc

E se o seu servidor suporta reescrita de URL (por exemplo mod_rewrite no Apache), então você pode esconder totalmente a parte script.

Personagens de URLs deve estar em UTF-8, urlencoded byte a byte:

/mot%C3%B6rhead   # motörhead

RFC 6266 descreve o “ O uso do Content-Disposition cabeçalho campo no Hypertext Transfer Protocol (HTTP ) ”. Citando que:

6. Internacionalização considerações

O parâmetro “filename*” ( Seção 4.3 ), usando a codificação definida em [ RFC5987 ], permite que o servidor personagens de transmissão fora da ISO-8859-1 conjunto de caracteres, e também para especificar opcionalmente a linguagem em uso.

E na sua seção exemplos :

Este exemplo é o mesmo que o descrito acima, mas adicionando o "filename" parâmetro para a compatibilidade com os agentes do utilizador não implementar RFC 5987 :

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

Nota: Os agentes que não suportam a RFC 5987 codificação ignorar “filename*” quando ele ocorre depois de “filename”.

Na Apêndice D há também uma longa lista de sugestões para aumentar a interoperabilidade. Ele também aponta para um site que compara implementações . testes de passa-tudo atual adequados para nomes de arquivos comuns incluem:

  • attwithisofnplain : plain ISO-8859-1 nome de arquivo com aspas e sem codificação. Isso requer um nome de arquivo que é tudo o ISO-8859-1 e não contém sinais de percentagem, pelo menos não na frente de dígitos hexadecimais.
  • attfnboth : dois parâmetros na ordem descrita acima. Deve funcionar para a maioria dos nomes de arquivos na maioria dos navegadores, embora IE8 irá usar o parâmetro “filename”.

RFC 5987 em referências turno RFC 2231 , que descreve o formato real. 2231 é principalmente para e-mail, e 5987 nos diz que partes podem ser usados ??para cabeçalhos HTTP também. Não confunda isso com cabeçalhos MIME usados ??dentro de um multipart/form-data HTTP corpo , que é regido por RFC 2388 ( secção 4.4 em particular) e o HTML 5 projecto .

O documento a seguir ligada a partir o projecto RFC mencionado por Jim em sua resposta mais endereços a questão e definitivamente vale a pena uma nota direto aqui:

casos de teste para HTTP cabeçalho Content-Disposition e RFC 2231/2047 Encoding

em asp.net MVC2 i usar algo como isto:

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

Eu acho que se você não usar MVC (2) você pode apenas codificar o nome do arquivo usando

HttpUtility.UrlPathEncode(fileName)

Coloque o nome do arquivo entre aspas duplas. Resolveu o problema para mim. Como esta:

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

http://kb.mozillazine.org/Filenames_with_spaces_are_truncated_upon_download

Eu testei várias opções. Navegadores não suportam as especificações e agir de forma diferente, acredito aspas é a melhor opção.

Eu uso os seguintes trechos de código para codificar (assumindo fileName contém o nome do arquivo e extensão do arquivo, ou seja .: 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 ) );
}

Java:

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

Em ASP.NET Web API, eu URL codificar o nome do arquivo:

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 n&atilde;o fixa
IE 9 fixo

Eu testei o seguinte código em todos os principais navegadores, incluindo Explorers mais velhos (através do modo de compatibilidade), e funciona bem em todos os lugares:

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

Se você estiver usando uma nodejs backend você pode usar o seguinte código eu encontrei aqui

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

Eu acabei com o seguinte código no meu script "download.php" (com base em este blogPost e estes casos de teste ).

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

Isto usa a forma padrão de nome de arquivo = "..." Enquanto há apenas iso-latin1 e caracteres 'seguros' utilizados; se não, ele adiciona o nome do arquivo * = UTF-8 '' maneira url-codificado. De acordo com a este caso de teste específico , ele deve funcionar a partir MSIE9, e em FF recente, Chrome , Safari; na menor versão MSIE, deve oferecer filename contendo a versão ISO8859-1 do nome do arquivo, com sublinhados em personagens não neste codificação.

Nota final: o máx. tamanho para cada campo de cabeçalho é 8190 bytes em Apache. UTF-8 pode ser de até quatro bytes por caractere; após rawurlencode, é x3 = 12 bytes por uma personagem. Muito ineficiente, mas deve ainda ser teoricamente possível ter mais de 600 "sorrisos" % F0% 9F% 98% 81 no nome do arquivo.

No PHP esta fez isso por mim (assumindo que o nome do arquivo é UTF8 codificado):

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

Testado contra IE8-11, Firefox e Chrome.
Se o navegador pode interpretar filename * = utf-8 ele irá usar a versão UTF8 do nome do arquivo, então ele irá usar o nome do arquivo decodificado. Se o seu nome de arquivo contém caracteres que não podem ser representados em ISO-8859-1 que você pode querer considerar o uso iconv vez.

Clássico ASP Solution

A maioria dos navegadores modernos suportam passar o Filename como UTF-8 agora, mas como foi o caso com um arquivo de upload uso solução I que foi baseado em FreeASPUpload.Net (site não existir mais, link aponta para archive.org ) isso não iria funcionar como a análise do binário contou com a leitura cordas único byte ASCII codificados, que funcionou bem quando você passou UTF-8 codificado dados até chegar a caracteres ASCII não suporta.

No entanto, eu era capaz de encontrar uma solução para obter o código para ler e analisar o binário como 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

O crédito vai para arquivo ASP Pure Carregar por implementar a função BytesToString() de include_aspuploader.asp no meu próprio código I foi capaz de obter nomes de arquivos UTF-8 de trabalho.


Links Úteis

Apenas uma atualização desde que eu estava tentando tudo isso hoje em resposta a uma questão do cliente

  • Com exceção do Safari configurado para japonês, todos os navegadores nosso cliente testados funcionaram melhor com filename = text.pdf - em que o texto é um valor para o cliente serializado por ASP.Net/IIS em utf-8, sem codificação de URL. Por alguma razão, Safari configurado para Inglês aceitaria e devidamente salvar um arquivo com utf-8 nome japonês, mas que mesmo browser configurado para o japonês iria salvar o arquivo com as utf-8 caracteres não-interpretada. Todos os outros navegadores testados parecia funcionar melhor / fino (independentemente da configuração de idioma) com o nome do arquivo utf-8 codificado sem codificação url.
  • Eu não poderia encontrar um único navegador implementação Rfc5987 / 8187 em tudo . Eu testei com a mais recente Chrome, Firefox constrói mais IE 11 e Edge. Tentei configurar o cabeçalho com apenas filename * = utf-8''texturlencoded.pdf, definindo-o com tanto filename = text.pdf; nome do arquivo * = UTF-8''texturlencoded.pdf. Não uma característica de Rfc5987 / 8187 parecia estar sendo processado corretamente em qualquer um dos acima.

Nós tivemos um problema semelhante em uma aplicação web, e acabou lendo o nome do arquivo do <input type="file"> HTML, e definindo que, na forma de código URL em uma nova <input type="hidden"> HTML. É claro que tivemos para remover o caminho como "C: \ fakepath \" que é retornado por alguns navegadores.

É claro que isto não responde directamente questão PO, mas pode ser uma solução para os outros.

Eu normalmente URL-codificar (com% xx) os nomes dos arquivos, e parece funcionar em todos os navegadores. Você pode querer fazer alguns testes de qualquer maneira.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top