Comment définir le nom du fichier de téléchargement dans l'API Web ASP.NET


140

Dans ma classe ApiController, j'ai la méthode suivante pour télécharger un fichier créé par le serveur.

public HttpResponseMessage Get(int id)
{
    try
    {
        string dir = HttpContext.Current.Server.MapPath("~"); //location of the template file
        Stream file = new MemoryStream();
        Stream result = _service.GetMyForm(id, dir, file);
        if (result == null)
        {
            return Request.CreateResponse(HttpStatusCode.NotFound);
        }
        result.Position = 0;
        HttpResponseMessage response = new HttpResponseMessage();
        response.StatusCode = HttpStatusCode.OK;
        response.Content = new StreamContent(result);
        return response;
    }
    catch (IOException)
    {
        return Request.CreateResponse(HttpStatusCode.InternalServerError);
    }
}

Tout fonctionne parfaitement, sauf que le nom du fichier de téléchargement par défaut est son identifiant, de sorte que l'utilisateur devra peut-être taper son propre nom de fichier dans la boîte de dialogue Enregistrer sous à chaque fois. Existe-t-il un moyen de définir un nom de fichier par défaut dans le code ci-dessus?


1
pouvez-vous partager le code que vous avez utilisé pour appeler cette méthode?
Yasser Shaikh

@Yasser - il s'agit d'une méthode de contrôleur d'API Web - elle est probablement appelée via des requêtes HTTP entrant dans IIS et les analysant et trouvant des routes et une API Web appelant la méthode (et, bien sûr, elle est également appelée par des tests).
Dave Rael

que se passe-t-il dans GetMyForm ()? Conversion des fichiers en flux d'octets?
MSIslam

@MSIslam En quelque sorte. La fonction obtient des informations du formulaire de l'utilisateur et crée un fichier avant de convertir le flux résultant.
Tae-Sung Shin

Réponses:


289

Vous devez définir l'en- Content-Dispositiontête sur HttpResponseMessage:

HttpResponseMessage response = new HttpResponseMessage();
response.StatusCode = HttpStatusCode.OK;
response.Content = new StreamContent(result);
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
    FileName = "foo.txt"
};

21
Pour quiconque est curieux de connaître le type de disposition "attachement", la liste complète des types de disposition se trouve sur iana.org/assignments/cont-disp/cont-disp.xhtml
sfuqua

1
Vous avez une autre réponse au téléchargement d'un fichier ici. Est-ce important que vous utilisiez System.Net.Mime.ContentDispositionou ContentDispositionHeaderValue? L'un est-il plus actuel et plus préféré que l'autre?
Lumineux

@Luminous une réponse est pour ActionResult, un pourHttpResponseMessage
Weston

@weston votre réponse ne répond pas à ma question.
Lumineux

4
@Luminous "Est-ce que ça compte" et "Est-ce que l'un est plus actuel et plus préféré que l'autre?" Non et non. L'un est pour les MVC ActionResultet l'autre pour les WebApi HttpResponseMessage.
weston

27

EDIT: Comme mentionné dans un commentaire, Ma réponse ne tient pas compte des caractères qui doivent être échappés comme un ;. Vous devez utiliser la réponse acceptée par Darin si votre nom de fichier peut contenir un point-virgule.

Ajoutez un Response.AddHeader pour définir le nom du fichier

Response.AddHeader("Content-Disposition", "attachment; filename=*FILE_NAME*");

Remplacez simplement FILE_NAME par le nom du fichier.


2
Cela s'est avéré utile pour moi pour résoudre un problème similaire à celui qui pose la question. Dans mon cas, j'ai également trouvé utile de changer «pièce jointe» en «en ligne» afin qu'IE8 me donne la possibilité de toujours ouvrir le type de fichier en question.
Scott

2
Ne couvre pas la fuite. Et si le nom du fichier comprend un ;ou quelque chose d'autre avec une signification particulière?
Sam

Sam, au moment où j'ai écrit cette réponse il y a 3 ans, je ne savais pas que ma réponse devait gérer l'évasion. Merci de me l'avoir signalé, j'ai modifié ma réponse en expliquant que ma réponse ne tient pas compte de l'échappée. Mais en gardant ma réponse la même car elle semblait avoir aidé les gens.
KingPancake

8

Si vous voulez vous assurer que le nom de fichier est correctement encodé mais également éviter le WebApi HttpResponseMessage, vous pouvez utiliser ce qui suit:

Response.AddHeader("Content-Disposition", new System.Net.Mime.ContentDisposition("attachment") { FileName = "foo.txt" }.ToString());

Vous pouvez utiliser ContentDisposition ou ContentDispositionHeaderValue. L'appel de ToString sur une instance de l'un ou l'autre effectuera le codage des noms de fichiers pour vous.


6

Je pense que cela pourrait vous être utile.

Response.AddHeader("Content-Disposition", "attachment; filename=" + fileName)

4
Ne couvre pas la fuite. Et si le nom du fichier comprend un ;ou quelque chose d'autre avec une signification particulière?
Sam

3

Vous devez ajouter l'en-tête content-disposition à la réponse:

 response.StatusCode = HttpStatusCode.OK;
 response.Content = new StreamContent(result);
 response.AppendHeader("content-disposition", "attachment; filename=" + fileName);
 return response;

3
Ne couvre pas la fuite. Et si le nom du fichier comprend un ;ou quelque chose d'autre avec une signification particulière?
Sam

3

Si vous utilisez ASP.NET Core MVC, les réponses ci-dessus sont très légèrement modifiées ...

Dans ma méthode d'action (qui retourne async Task<JsonResult>) j'ajoute la ligne (n'importe où avant l'instruction return):

Response.Headers.Add("Content-Disposition", $"attachment; filename={myFileName}");

Ne couvre pas la fuite. Que faire si le nom de fichier comprend un; ou autre chose avec une signification particulière?
KoalaBear

2

Cela devrait faire:

Response.AddHeader("Content-Disposition", "attachment;filename="+ YourFilename)

2
Ne couvre pas la fuite. Et si le nom du fichier comprend un ;ou quelque chose d'autre avec une signification particulière?
Sam

0

Remarque: la dernière ligne est obligatoire.

Si nous n'avons pas spécifié Access-Control-Expose-Headers , nous n'obtiendrons pas le nom de fichier dans l'interface utilisateur.

FileInfo file = new FileInfo(FILEPATH);

HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
    FileName = file.Name
};
response.Content.Headers.Add("Access-Control-Expose-Headers", "Content-Disposition");

0

Compte tenu des réponses précédentes, il faut faire attention aux caractères globalisés.

Supposons que le nom du fichier soit: " Esdrújula prenda ñame - güena.jpg "

Résultat brut à télécharger: "Esdrújula prenda à ± ame - güena.jpg" [Moche]

Résultat HtmlEncode à télécharger: "Esdr & _250; jula prenda & _241; ame - g & _252; ena.jpg" [Ugly]

Résultat UrlEncode à télécharger: " Esdrújula + prenda + ñame + - + güena.jpg " [OK]

Ensuite, vous devez presque toujours utiliser UrlEncode sur le nom du fichier . De plus, si vous définissez l'en-tête de disposition de contenu en tant que chaîne directe, vous devez vous assurer que l'encadrement est entre guillemets pour éviter les problèmes de compatibilité du navigateur.

Response.AddHeader("Content-Disposition", $"attachment; filename=\"{HttpUtility.UrlEncode(YourFilename)}\"");

ou avec l'aide de classe:

var cd = new ContentDisposition("attachment") { FileName = HttpUtility.UrlEncode(resultFileName) };
Response.AddHeader("Content-Disposition", cd.ToString());

Le System.Net.Mime. La classe ContentDisposition prend en charge les citations.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.