Comment retourner un fichier (FileContentResult) dans ASP.NET WebAPI

dans un contrôleur MVC régulier, nous pouvons afficher pdf avec un FileContentResult .

public FileContentResult Test(TestViewModel vm)
{
    var stream = new MemoryStream();
    //... add content to the stream.

    return File(stream.GetBuffer(), "application/pdf", "test.pdf");
}

mais comment la transformer en ApiController ?

[HttpPost]
public IHttpActionResult Test(TestViewModel vm)
{
     //...
     return Ok(pdfOutput);
}

voici ce que j'ai essayé mais cela ne semble pas fonctionner.

[HttpGet]
public IHttpActionResult Test()
{
    var stream = new MemoryStream();
    //...
    var content = new StreamContent(stream);
    content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
    content.Headers.ContentLength = stream.GetBuffer().Length;
    return Ok(content);            
}

Le résultat affiché dans le navigateur est:

{"Headers":[{"Key":"Content-Type","Value":["application/pdf"]},{"Key":"Content-Length","Value":["152844"]}]}

et il y a un post similaire sur SO: retour binaire fichier du contrôleur dans ASP.NET API Web . Il parle de la sortie d'un fichier existant. Mais je ne pouvais pas le faire fonctionner avec un ruisseau.

des suggestions?

126
demandé sur Community 2014-09-25 16:37:01

5 réponses

au Lieu de retourner StreamContent comme le Content , je peux le faire fonctionner avec ByteArrayContent .

[HttpGet]
public HttpResponseMessage Generate()
{
    var stream = new MemoryStream();
    // processing the stream.

    var result = new HttpResponseMessage(HttpStatusCode.OK)
    {
        Content = new ByteArrayContent(stream.ToArray())
    };
    result.Content.Headers.ContentDisposition =
        new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment")
    {
        FileName = "CertificationCard.pdf"
    };
    result.Content.Headers.ContentType =
        new MediaTypeHeaderValue("application/octet-stream");

    return result;
}
142
répondu Blaise 2017-11-29 10:51:59

si vous voulez retourner IHttpActionResult vous pouvez le faire comme ceci:

[HttpGet]
public IHttpActionResult Test()
{
    var stream = new MemoryStream();

    var result = new HttpResponseMessage(HttpStatusCode.OK)
    {
        Content = new ByteArrayContent(stream.GetBuffer())
    };
    result.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment")
    {
        FileName = "test.pdf"
    };
    result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");

    var response = ResponseMessage(result);

    return response;
}
59
répondu Ogglas 2016-06-15 23:42:00

cette question m'a aidé.

alors, essayez ceci:

Code du contrôleur:

[HttpGet]
public HttpResponseMessage Test()
{
    var path = System.Web.HttpContext.Current.Server.MapPath("~/Content/test.docx");;
    HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);
    var stream = new FileStream(path, FileMode.Open);
    result.Content = new StreamContent(stream);
    result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
    result.Content.Headers.ContentDisposition.FileName = Path.GetFileName(path);
    result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
    result.Content.Headers.ContentLength = stream.Length;
    return result;          
}

afficher le balisage Html (avec l'événement de clic et l'url simple):

<script type="text/javascript">
    $(document).ready(function () {
        $("#btn").click(function () {
            // httproute = "" - using this to construct proper web api links.
            window.location.href = "@Url.Action("GetFile", "Data", new { httproute = "" })";
        });
    });
</script>


<button id="btn">
    Button text
</button>

<a href=" @Url.Action("GetFile", "Data", new { httproute = "" }) ">Data</a>
35
répondu aleha 2018-06-15 06:40:30

Je ne sais pas exactement quelle partie blâmer, mais voici pourquoi MemoryStream ne fonctionne pas pour vous:

comme vous écrivez à MemoryStream , il augmente sa propriété Position . Le constructeur de StreamContent prend en compte le courant du flux Position . Donc si vous écrivez au flux, puis le passez à StreamContent , la réponse partira du néant à la fin du flux.

il y a deux façons de réparer correctement ce:

1) Construire le contenu, écrire au flux

[HttpGet]
public HttpResponseMessage Test()
{
    var stream = new MemoryStream();
    var response = Request.CreateResponse(HttpStatusCode.OK);
    response.Content = new StreamContent(stream);
    // ...
    // stream.Write(...);
    // ...
    return response;
}

2) écrire à des flux, de la réinitialisation de la position, de construction de contenu

[HttpGet]
public HttpResponseMessage Test()
{
    var stream = new MemoryStream();
    // ...
    // stream.Write(...);
    // ...
    stream.Position = 0;

    var response = Request.CreateResponse(HttpStatusCode.OK);
    response.Content = new StreamContent(stream);
    return response;
}

2) semble un peu mieux si vous avez un nouveau flux, 1) est plus simple si votre flux ne commence pas à 0

5
répondu M.Stramm 2017-07-05 09:16:02

Pour moi, c'était la différence entre

var response = Request.CreateResponse(HttpStatusCode.OK, new StringContent(log, System.Text.Encoding.UTF8, "application/octet-stream");

et

var response = Request.CreateResponse(HttpStatusCode.OK);
response.Content = new StringContent(log, System.Text.Encoding.UTF8, "application/octet-stream");

le premier retournait la représentation JSON de StringContent: {"Headers": [{"Key": "Content-Type", "Value": ["application/octet-stream; charset=utf-8"]}]

alors que le second retournait le dossier proprement dit.

il semble que la demande.CreateResponse a une surcharge qui prend une chaîne comme seconde paramètre et cela semble avoir été ce qui causait L'objet StringContent lui-même d'être rendu comme une chaîne, au lieu du contenu réel.

2
répondu EnderWiggin 2018-02-05 14:49:37