Как вернуть файл (FileContentResult) в ASP.NET WebAPI
в обычном контроллере MVC мы можем выводить pdf с помощью FileContentResult
.
public FileContentResult Test(TestViewModel vm)
{
var stream = new MemoryStream();
//... add content to the stream.
return File(stream.GetBuffer(), "application/pdf", "test.pdf");
}
но как мы можем превратить его в ApiController
?
[HttpPost]
public IHttpActionResult Test(TestViewModel vm)
{
//...
return Ok(pdfOutput);
}
вот что я пробовал, но, похоже, это не работает.
[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);
}
возвращенный результат, отображаемый в браузере:
{"Headers":[{"Key":"Content-Type","Value":["application/pdf"]},{"Key":"Content-Length","Value":["152844"]}]}
и есть аналогичный пост на SO:возврат двоичного файла с контроллера в ASP.NET Web API . Он говорит о выводе существующего файла. но я не мог заставить его работать с потоком.
какие предложения?
5 ответов
вместо возврата StreamContent
как Content
, Я могу заставить его работать с 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;
}
Если вы хотите вернуть IHttpActionResult
вы можете сделать это так:
[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;
}
этой вопрос помог мне.
Итак, попробуйте это:
код контроллера:
[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 (с событием щелчка и простым 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>
Я не совсем уверен, какая часть виновата, но вот почему MemoryStream
не работает:
как вы пишете в MemoryStream
, Он увеличивает его Position
собственность.
Конструктор StreamContent
учитывает текущий поток Position
. Поэтому, если вы пишете в поток, то передайте его в StreamContent
ответ будет запустить из небытия в конце потока.
есть два способа исправить это:
1) Содержание конструкции, пишет к поток
[HttpGet]
public HttpResponseMessage Test()
{
var stream = new MemoryStream();
var response = Request.CreateResponse(HttpStatusCode.OK);
response.Content = new StreamContent(stream);
// ...
// stream.Write(...);
// ...
return response;
}
2) запись в поток, сбросить позицию, построить контент
[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) выглядит немного лучше, если у вас есть свежий поток, 1) проще, если ваш поток не начинается с 0
для меня это была разница между
var response = Request.CreateResponse(HttpStatusCode.OK, new StringContent(log, System.Text.Encoding.UTF8, "application/octet-stream");
и
var response = Request.CreateResponse(HttpStatusCode.OK);
response.Content = new StringContent(log, System.Text.Encoding.UTF8, "application/octet-stream");
первый возвращал JSON-представление StringContent: {"Headers": [{"Key": "Content-Type", "Value": ["application / octet-stream; charset=utf-8"]}]}
в то время как второй возвращал файл правильно.
Кажется, что запрос.CreateResponse имеет перегрузку, которая принимает строку в качестве второго параметра, и это, похоже, было причиной Сам объект StringContent будет отображаться как строка, а не фактическое содержимое.