Domanda

Applicazioni Web che vogliono forzare l'esistenza di una risorsa scaricato piuttosto che direttamente reso in un problema del browser Web a Content-Disposition intestazione nella risposta HTTP del modulo:

Content-Disposition: attachment; filename=NOME DEL FILE

IL filename Il parametro può essere utilizzato per suggerire un nome per il file in cui la risorsa viene scaricata dal browser. RFC2183 (Contenuto-Disposizione), tuttavia, precisa in sezione 2.3 (Il parametro del nome file) che il nome del file può utilizzare solo caratteri US-ASCII:

L'attuale [RFC 2045] la grammatica limita i valori dei parametri (e quindi i nomi di file di disposizione del contenuto) a US-ASCII.Riconosciamo la grande desiderabilità di consentire set di caratteri arbitrari nei file di file, ma è oltre lo scopo di questo documento definire i meccanismi necessari.

Esistono prove empiriche, tuttavia, che i browser Web più popolari oggi sembrano consentire caratteri non-US-ASCII ma (per la mancanza di uno standard) non sono d'accordo sullo schema di codifica e sulla specifica del set di caratteri del nome del file.La domanda è quindi: quali sono i vari schemi e codifiche utilizzati dai browser più diffusi se il nome del file "naïvefile" (senza virgolette e dove la terza lettera è U+00EF) dovesse essere codificato nell'intestazione Content-Disposition?

Ai fini di questa domanda, browser più diffusi essendo:

  • Firefox
  • Internet Explorer
  • Safari
  • Google Chrome
  • musica lirica
È stato utile?

Soluzione

Si discute di questo, inclusi i collegamenti ai test dei browser e alla compatibilità con le versioni precedenti, nella proposta RFC5987, "Set di caratteri e codifica della lingua per i parametri del campo di intestazione HTTP (Hypertext Transfer Protocol)."

RFC2183 indica che tali intestazioni dovrebbero essere codificate secondo RFC2184, che è stato reso obsoleto da RFC2231, coperto dal progetto RFC di cui sopra.

Altri suggerimenti

So che questo è un vecchio post ma è ancora molto attuale.Ho scoperto che i browser moderni supportano rfc5987, che consente la codifica utf-8, codificata in percentuale (codificata URL).Quindi Naïve file.txt diventa:

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

Safari (5) non lo supporta.Dovresti invece utilizzare lo standard Safari di scrivere il nome del file direttamente nell'intestazione codificata utf-8:

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

Anche IE8 e versioni precedenti non lo supportano ed è necessario utilizzare lo standard IE della codifica utf-8, codifica percentuale:

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

In ASP.Net utilizzo il seguente codice:

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

Ho testato quanto sopra utilizzando IE7, IE8, IE9, Chrome 13, Opera 11, FF5, Safari 5.

Aggiornamento Novembre 2013:

Ecco il codice che utilizzo attualmente.Devo ancora supportare IE8, quindi non posso liberarmi della prima parte.Si scopre che i browser su Android utilizzano il gestore dei download integrato in Android e non è in grado di analizzare in modo affidabile i nomi dei file nel modo standard.

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

Quanto sopra ora testato in IE7-11, Chrome 32, Opera 12, FF25, Safari 6, utilizzando questo nome file per il download:你好abcABCæøåÆØÅäöüïëêîâéíáóúýñ½§!#¤%&()=`@£$€{[]}+´¨^~'-_,;.txt

Su IE7 funziona per alcuni caratteri ma non per tutti.Ma chi se ne frega di IE7 al giorno d'oggi?

Questa è la funzione che utilizzo per generare nomi di file sicuri per Android.Tieni presente che non so quali caratteri siano supportati su Android ma che ho testato che funzionino di sicuro:

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:Ho provato in IE7 e IE8 e ho scoperto che non avevo bisogno di evitare l'apostrofo (').Hai un esempio in cui fallisce?

@Dave Van den Eynde:La combinazione dei due nomi di file su una riga secondo RFC6266 funziona tranne che per Android e IE7+8 e ho aggiornato il codice per riflettere questo.Grazie per il suggerimento

@Thilo:Nessuna idea di GoodReader o di qualsiasi altro non browser.Potresti avere un po' di fortuna usando l'approccio Android.

@Alex Zhukovskiy:Non so perché, ma come discusso su Collegare non sembra funzionare molto bene.

Esiste un'alternativa semplice e molto robusta: utilizza un URL che contenga il nome file desiderato.

Quando il nome dopo l'ultima barra è quello che desideri, non hai bisogno di intestazioni aggiuntive!

Questo trucco funziona:

/real_script.php/fake_filename.doc

E se il tuo server supporta la riscrittura degli URL (ad es. mod_rewrite in Apache) allora puoi nascondere completamente la parte dello script.

I caratteri negli URL devono essere in UTF-8, codificati urlen byte per byte:

/mot%C3%B6rhead   # motörhead

RFC6266 descrive il “Utilizzo del campo Content-Disposition Header nell'Hypertext Transfer Protocol (HTTP)”.Citando da ciò:

6.Considerazioni sull'internazionalizzazione

IL "filename*"parametro (Sezione 4.3), usando la codifica definita in [RFC5987], consente al server di trasmettere caratteri al di fuori del set di caratteri ISO-8859-1 e anche di specificare facoltativamente la lingua in uso.

E nel loro sezione esempi:

Questo esempio è lo stesso di quello sopra, ma aggiungendo il parametro "nome file" per la compatibilità con gli agenti utente che non sono implementati RFC5987:

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

Nota:Quegli user agent che non supportano il file RFC5987 la codifica ignora "filename*" quando si verifica dopo "filename”.

In Appendice D c'è anche un lungo elenco di suggerimenti per aumentare l'interoperabilità.Indica anche un sito che confronta le implementazioni.Gli attuali test passa-tutto adatti per nomi di file comuni includono:

  • attwithisofnplain:nome file ISO-8859-1 semplice con virgolette doppie e senza codifica.Ciò richiede un nome file che sia tutto ISO-8859-1 e non contenga segni di percentuale, almeno non davanti a cifre esadecimali.
  • attfnentrambi:due parametri nell'ordine sopra descritto.Dovrebbe funzionare per la maggior parte dei nomi di file sulla maggior parte dei browser, anche se IE8 utilizzerà il "filename"parametro.

Quello RFC5987 a loro volta riferimenti RFC2231, che descrive il formato effettivo.2231 è principalmente per la posta e 5987 ci dice quali parti possono essere utilizzate anche per le intestazioni HTTP.Non confonderlo con le intestazioni MIME utilizzate all'interno di a multipart/form-data HTTP corpo, che è disciplinato da RFC2388 (sezione 4.4 in particolare) e il Bozza HTML5.

Il seguente documento collegato da il progetto di RFC menzionato da Jim nella sua risposta affronta ulteriormente la domanda e merita sicuramente una nota diretta qui:

Casi di test per l'intestazione HTTP Content-Disposition e la codifica RFC 2231/2047

in asp.net mvc2 utilizzo qualcosa del genere:

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

Immagino che se non usi mvc(2) potresti semplicemente codificare il nome del file usando

HttpUtility.UrlPathEncode(fileName)

Metti il ​​nome del file tra virgolette doppie.Risolto il problema per me.Come questo:

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

http://kb.mozillazine.org/Filenames_with_spaces_are_truncated_upon_download

Ho testato più opzioni.I browser non supportano le specifiche e si comportano diversamente, credo che le virgolette doppie siano l'opzione migliore.

Utilizzo i seguenti frammenti di codice per la codifica (assumendo nome del file contiene il nome del file e l'estensione del file, ovvero:prova.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 ) );
}

Giava:

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

Nell'API Web ASP.NET, codifico l'URL del nome file:

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

Ho testato il seguente codice in tutti i principali browser, inclusi i meno recenti Explorer (tramite la modalità compatibilità), e funziona bene ovunque:

$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 stai utilizzando un backend nodejs puoi utilizzare il seguente codice che ho trovato Qui

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

Alla fine ho ottenuto il seguente codice nel mio script "download.php" (basato su questo post sul blog E questi casi di prova).

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

Questo utilizza il modo standard di filename="..." fintanto che vengono utilizzati solo caratteri iso-latin1 e "safe";in caso contrario, aggiunge il modo filename*=UTF-8'' con codifica URL.Secondo questo specifico caso di prova, dovrebbe funzionare da MSIE9 in su e sui recenti FF, Chrome, Safari;nella versione MSIE inferiore, dovrebbe offrire un nome file contenente la versione ISO8859-1 del nome file, con trattini bassi sui caratteri non presenti in questa codifica.

Nota finale:il massimola dimensione per ciascun campo di intestazione è 8190 byte su Apache.UTF-8 può contenere fino a quattro byte per carattere;dopo rawurlencode, è x3 = 12 byte per un carattere.Abbastanza inefficiente, ma teoricamente dovrebbe essere comunque possibile avere più di 600 "sorrisi" %F0%9F%98%81 nel nome del file.

In PHP questo lo ha fatto per me (supponendo che il nome del file sia codificato UTF8):

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

Testato contro IE8-11, Firefox e Chrome.
Se il browser può interpretare nome file*=utf-8 utilizzerà la versione UTF8 del nome file, altrimenti utilizzerà il nome file decodificato.Se il nome del tuo file contiene caratteri che non possono essere rappresentati in ISO-8859-1 potresti prendere in considerazione l'utilizzo iconv Invece.

Soluzione ASP classica

La maggior parte dei browser moderni supporta il passaggio del file Filename COME UTF-8 now ma, come nel caso della soluzione di caricamento file che utilizzo, era basata su FreeASPUload.Net (il sito non esiste più, il link punta a archivio.org) non funzionerebbe poiché l'analisi del binario si basava sulla lettura di stringhe codificate ASCII a byte singolo, che funzionava correttamente quando si passavano dati codificati UTF-8 fino ad arrivare ai caratteri che ASCII non supporta.

Tuttavia sono riuscito a trovare una soluzione per far sì che il codice leggesse e analizzasse il binario come 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

Il merito va a Caricamento file ASP puro implementando il BytesToString() funzione da include_aspuploader.asp nel mio codice sono riuscito a ottenere UTF-8 nomi di file funzionanti.


link utili

Solo un aggiornamento poiché stavo provando tutte queste cose oggi in risposta a un problema del cliente

  • Ad eccezione di Safari configurato per il giapponese, tutti i browser testati dai nostri clienti hanno funzionato meglio con filename=text.pdf, dove text è un valore cliente serializzato da ASP.Net/IIS in utf-8 senza codifica URL.Per qualche motivo, Safari configurato per l'inglese accetterebbe e salverebbe correttamente un file con nome giapponese utf-8, ma lo stesso browser configurato per il giapponese salverebbe il file con i caratteri utf-8 non interpretati.Tutti gli altri browser testati sembravano funzionare meglio/bene (indipendentemente dalla configurazione della lingua) con il nome file utf-8 codificato senza codifica URL.
  • Non sono riuscito a trovare un singolo browser che implementi Rfc5987/8187 affatto.Ho testato con le ultime build di Chrome e Firefox oltre a IE 11 ed Edge.Ho provato a impostare l'intestazione solo con filename*=utf-8''texturlencoded.pdf, impostandola sia con filename=text.pdf;nomefile*=utf-8''texturlencoded.pdf.Nessuna caratteristica di Rfc5987/8187 sembrava essere elaborata correttamente in nessuna delle precedenti.

Abbiamo riscontrato un problema simile in un'applicazione Web e alla fine abbiamo letto il nome del file dall'HTML <input type="file">, e impostandolo nel formato con codifica URL in un nuovo HTML <input type="hidden">.Ovviamente abbiamo dovuto rimuovere il percorso come "C:\fakepath\" restituito da alcuni browser.

Ovviamente questo non risponde direttamente alla domanda dei PO, ma potrebbe essere una soluzione per altri.

Normalmente codifico tramite URL (con %xx) i nomi dei file e sembra funzionare in tutti i browser.Potresti comunque fare qualche prova.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top