web-dev-qa-db-fra.com

Retournez PDF au navigateur en utilisant le noyau Asp.net

J'ai créé l'API Wep dans le noyau ASP.Net pour renvoyer le PDF. Voici mon code:

public HttpResponseMessage Get(int id)
{
    var response = new HttpResponseMessage(System.Net.HttpStatusCode.OK);           
    var stream = new System.IO.FileStream(@"C:\Users\shoba_eswar\Documents\REquest.pdf", System.IO.FileMode.Open);
    response.Content = new StreamContent(stream);
    response.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");
    response.Content.Headers.ContentDisposition.FileName = "NewTab";
    response.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/pdf");
    return response;
}

Mais il ne renvoie que la réponse JSON:

{
   "version":{
      "major":1,
      "minor":1,
      "build":-1,
      "revision":-1,
      "majorRevision":-1,
      "minorRevision":-1
   },
   "content":{
      "headers":[
         {
            "key":"Content-Disposition",
            "value":[
               "attachment; filename=NewTab"
            ]
         },
         {
            "key":"Content-Type",
            "value":[
               "application/pdf"
            ]
         }
      ]
   },
   "statusCode":200,
   "reasonPhrase":"OK",
   "headers":[

   ],
   "requestMessage":null,
   "isSuccessStatusCode":true
}

Suis-je en train de faire quelque chose de mal ici?

17
Shoba

Comme expliqué dans ASP.NET Core HTTPRequestMessage renvoie un étrange message JSON , ASP.NET Core ne prend pas en charge le renvoi d'un HttpResponseMessage (quel package avez-vous installé pour accéder à ce type?).

Pour cette raison, le sérialiseur écrit simplement toutes les propriétés publiques de HttpResponseMessage dans la sortie, comme il le ferait avec tout autre type de réponse non pris en charge.

Pour prendre en charge les réponses personnalisées, vous devez renvoyer un type d'implémentation IActionResult-. Il y a beaucoup de ceux-ci . Dans votre cas, j'examinerais le FileStreamResult :

public IActionResult Get(int id)
{
    var stream = new FileStream(@"path\to\file", FileMode.Open);
    return new FileStreamResult(stream, "application/pdf");     
}

Ou utilisez simplement un PhysicalFileResult , où le flux est géré pour vous:

public IActionResult Get(int id)
{
    return new PhysicalFileResult(@"path\to\file", "application/pdf");
}

Bien sûr, tout cela peut être simplifié en utilisant des méthodes d'assistance, telles que Controller.File():

public IActionResult Get(int id)
{
    var stream = new FileStream(@"path\to\file", FileMode.Open);
    return File(stream, "application/pdf", "FileDownloadName.ext");
}

Cela résume simplement la création d'un FileContentResult ou FileStreamResult (pour cette surcharge, ce dernier).

Ou si vous convertissez une ancienne application MVC ou Web API et que vous ne voulez pas convertir tout votre code à la fois, ajoutez une référence à WebApiCompatShim (NuGet) et enveloppez votre code actuel dans un - ResponseMessageResult :

public IActionResult Get(int id)
{
    var response = new HttpResponseMessage(HttpStatusCode.OK);           
    var stream = ...
    response.Content...

    return new ResponseMessageResult(response);
}

Si vous ne souhaitez pas utiliser return File(fileName, contentType, fileDownloadName), le FileStreamResult ne prend pas en charge la définition de l'en-tête content-disposition depuis le constructeur ou via les propriétés.

Dans ce cas, vous devrez ajouter cet en-tête de réponse à la réponse vous-même avant de renvoyer le résultat du fichier:

var contentDisposition = new ContentDispositionHeaderValue("attachment");
contentDisposition.SetHttpFileName("foo.txt");
Response.Headers[HeaderNames.ContentDisposition] = contentDisposition.ToString();
50
CodeCaster

Je n'ai pas pu commenter la réponse de CodeCaster car ma réputation n'est pas assez élevée. En essayant

public IActionResult Get(int id)
{
    using (var stream = new FileStream(@"path\to\file", FileMode.Open))
    {
        return File(stream, "application/pdf", "FileDownloadName.ext");
    }       
} 

nous avons un

ObjectDisposedException: impossible d'accéder à un objet supprimé. Nom de l'objet: 'Impossible d'accéder à un fichier fermé.'. System.IO.FileStream.BeginRead (octet [] tableau, int offset, int numBytes, rappel AsyncCallback, état de l'objet)

Nous avons supprimé l'utilisation

   [HttpGet]
   [Route("getImageFile")]
   public IActionResult GetWorkbook()
   {
        var stream = new FileStream(@"pathToFile", FileMode.Open);
        return File(stream, "image/png", "image.png");
   }

Et ça a marché. Il s'agit d'ASP.NET Core 2.1 exécuté dans IIS Express.

6
Bernhard Maertl

Je n'ai pas assez de réputation pour poster ceci en tant que commentaire, donc en tant que réponse. Les 3 premières solutions de @CodeCaster et la solution de @BernhardMaertl sont correctes.

Cependant, pour quelqu'un qui ne travaille pas souvent avec des fichiers (comme moi), veuillez noter que si le processus exécutant ce code (par exemple l'API) n'a que des autorisations de lecture sur le fichier, vous devrez le spécifier comme troisième paramètre lors de la création votre FileStream, sinon le comportement par défaut consiste à ouvrir le fichier en lecture/écriture et vous obtiendrez une exception car vous ne disposez pas des autorisations d'écriture.

La 3e solution de @CodeCaster ressemblerait alors à ceci:

public IActionResult Get(int id)
{
    var stream = new FileStream(@"path\to\file", FileMode.Open, FileAccess.Read);
    return File(stream, "application/pdf", "FileDownloadName.ext");
}
1