In a regular MVC controller, we can output pdf with a FileContentResult
.
public FileContentResult Test(TestViewModel vm)
{
var stream = new MemoryStream();
//... add content to the stream.
return File(stream.GetBuffer(), "application/pdf", "test.pdf");
}
But how can we change it into an ApiController
?
[HttpPost]
public IHttpActionResult Test(TestViewModel vm)
{
//...
return Ok(pdfOutput);
}
Here is what I've tried but it doesn't seem to work.
[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);
}
The returned result displayed in the browser is:
{"Headers":[{"Key":"Content-Type","Value":["application/pdf"]},{"Key":"Content-Length","Value":["152844"]}]}
And there is a similar post on SO: Returning binary file from controller in ASP.NET Web API . It talks about output an existing file. But I could not make it work with a stream.
Any suggestions?
Instead of returning
StreamContent
as theContent
, I can make it work withByteArrayContent
.If you want to return
IHttpActionResult
you can do it like this:I am not exactly sure which part to blame, but here's why
MemoryStream
doesn't work for you:As you write to
MemoryStream
, it increments it'sPosition
property. The constructor ofStreamContent
takes into account the stream's currentPosition
. So if you write to the stream, then pass it toStreamContent
, the response will start from the nothingness at the end of the stream.There's two ways to properly fix this:
1) construct content, write to stream
2) write to stream, reset position, construct content
2) looks a little better if you have a fresh Stream, 1) is simpler if your stream does not start at 0
Here is an implementation that streams the file's content out without buffering it (buffering in byte[] / MemoryStream, etc. can be a server problem if it's a big file).
It can be simply used like this:
For me it was the difference between
and
The first one was returning the JSON representation of StringContent: {"Headers":[{"Key":"Content-Type","Value":["application/octet-stream; charset=utf-8"]}]}
While the second one was returning the file proper.
It seems that Request.CreateResponse has an overload that takes a string as the second parameter and this seems to have been what was causing the StringContent object itself to be rendered as a string, instead of the actual content.
This question helped me.
So, try this:
Controller code:
View Html markup (with click event and simple url):