In einem normalen MVC-Controller können wir PDF-Dateien mit einem FileContentResult
ausgeben.
public FileContentResult Test(TestViewModel vm)
{
var stream = new MemoryStream();
//... add content to the stream.
return File(stream.GetBuffer(), "application/pdf", "test.pdf");
}
Aber wie können wir es in ein ApiController
verwandeln?
[HttpPost]
public IHttpActionResult Test(TestViewModel vm)
{
//...
return Ok(pdfOutput);
}
Folgendes habe ich versucht, aber es scheint nicht zu funktionieren.
[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);
}
Das im Browser angezeigte Ergebnis ist:
{"Headers":[{"Key":"Content-Type","Value":["application/pdf"]},{"Key":"Content-Length","Value":["152844"]}]}
Und es gibt einen ähnlichen Beitrag zu SO: Rückgabe der Binärdatei vom Controller in der ASP.NET-Web-API . Es geht um die Ausgabe einer vorhandenen Datei. Aber ich konnte es nicht mit einem Stream zum Laufen bringen.
Irgendwelche Vorschläge?
Anstatt StreamContent
als Content
zurückzugeben, kann ich dafür sorgen, dass es mit ByteArrayContent
funktioniert.
[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;
}
Wenn Sie IHttpActionResult
zurückgeben möchten, können Sie dies folgendermaßen tun:
[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;
}
Diese Frage hat mir geholfen.
Also probieren Sie Folgendes:
Controller-Code:
[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;
}
HTML-Markup anzeigen (mit Klickereignis und einfacher URL):
<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>
Ich bin mir nicht sicher, welcher Teil schuld ist, aber hier ist der Grund, warum MemoryStream
bei Ihnen nicht funktioniert:
Wenn Sie in MemoryStream
schreiben, wird die Eigenschaft Position
erhöht. Der Konstruktor von StreamContent
berücksichtigt den aktuellen Position
des Streams. Wenn Sie also in den Stream schreiben und ihn an StreamContent
übergeben, beginnt die Antwort mit dem Nichts am Ende des Streams.
Es gibt zwei Möglichkeiten, dies zu beheben:
1) konstruiere Inhalte, schreibe in den Stream
[HttpGet]
public HttpResponseMessage Test()
{
var stream = new MemoryStream();
var response = Request.CreateResponse(HttpStatusCode.OK);
response.Content = new StreamContent(stream);
// ...
// stream.Write(...);
// ...
return response;
}
2) In Stream schreiben, Position zurücksetzen, Inhalt erstellen
[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) Sieht ein bisschen besser aus, wenn Sie einen neuen Stream haben, 1) ist einfacher, wenn Ihr Stream nicht bei 0 beginnt
Hier ist eine Implementierung, die den Inhalt der Datei streamt, ohne ihn zu puffern (das Puffern in Byte []/MemoryStream usw. kann ein Serverproblem sein, wenn es sich um eine große Datei handelt).
public class FileResult : IHttpActionResult
{
public FileResult(string filePath)
{
if (filePath == null)
throw new ArgumentNullException(nameof(filePath));
FilePath = filePath;
}
public string FilePath { get; }
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
var response = new HttpResponseMessage(HttpStatusCode.OK);
response.Content = new StreamContent(File.OpenRead(FilePath));
var contentType = MimeMapping.GetMimeMapping(Path.GetExtension(FilePath));
response.Content.Headers.ContentType = new MediaTypeHeaderValue(contentType);
return Task.FromResult(response);
}
}
Es kann einfach so verwendet werden:
public class MyController : ApiController
{
public IHttpActionResult Get()
{
string filePath = GetSomeValidFilePath();
return new FileResult(filePath);
}
}
Für mich war es der Unterschied zwischen
var response = Request.CreateResponse(HttpStatusCode.OK, new StringContent(log, System.Text.Encoding.UTF8, "application/octet-stream");
und
var response = Request.CreateResponse(HttpStatusCode.OK);
response.Content = new StringContent(log, System.Text.Encoding.UTF8, "application/octet-stream");
Der erste gab die JSON-Darstellung von StringContent zurück: {"Headers": [{"Key": "Content-Type", "Value": ["application/octet-stream; charset = utf-8"]}}
Während der zweite die eigentliche Datei zurückgab.
Es scheint, dass Request.CreateResponse eine Überladung hat, die eine Zeichenfolge als zweiten Parameter verwendet. Dies hat anscheinend dazu geführt, dass das StringContent-Objekt selbst als Zeichenfolge statt als tatsächlicher Inhalt gerendert wurde.