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?

Tae-Sung Shin
la source
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"
};
Darin Dimitrov
la source
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.

KingPancake
la source
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.

Sorenhk
la source
6

Je pense que cela pourrait vous être utile.

Response.AddHeader("Content-Disposition", "attachment; filename=" + fileName)
Jarek
la source
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;
Carl Raymond
la source
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}");
Peter
la source
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)
tucaz
la source
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");
Chandan YS
la source
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.

Asereware
la source