Em um controlador MVC comum, podemos produzir pdf com a FileContentResult
.
public FileContentResult Test(TestViewModel vm)
{
var stream = new MemoryStream();
//... add content to the stream.
return File(stream.GetBuffer(), "application/pdf", "test.pdf");
}
Mas como podemos transformá-lo em um ApiController
?
[HttpPost]
public IHttpActionResult Test(TestViewModel vm)
{
//...
return Ok(pdfOutput);
}
Aqui está o que eu tentei, mas parece não funcionar.
[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);
}
O resultado retornado exibido no navegador é:
{"Headers":[{"Key":"Content-Type","Value":["application/pdf"]},{"Key":"Content-Length","Value":["152844"]}]}
E há uma postagem semelhante no SO: retornando arquivo binário do controlador na API da Web do ASP.NET . Ele fala sobre a saída de um arquivo existente. Mas não consegui fazê-lo funcionar com um fluxo.
Alguma sugestão?
c#
asp.net
asp.net-mvc
asp.net-web-api
Blaise
fonte
fonte
Respostas:
Em vez de retornar
StreamContent
comoContent
, posso fazê-lo funcionarByteArrayContent
.fonte
MemoryStream.GetBuffer()
na verdade, retorna o buffer do MemoryStream, que geralmente é maior que o conteúdo do fluxo (para tornar as inserções eficientes).MemoryStream.ToArray()
retorna o buffer truncado para o tamanho do conteúdo.byte[]
buffers? Seus usuários podem facilmente executar seu aplicativo sem memória.Se você quiser retornar,
IHttpActionResult
faça o seguinte:fonte
Esta pergunta me ajudou.
Então, tente o seguinte:
Código do controlador:
Veja a marcação HTML (com evento click e URL simples):
fonte
FileStream
para um arquivo existente no servidor. É um pouco diferente deMemoryStream
. Mas obrigado pela contribuição.FileStream
mas falha comMemoryStream
. Basicamente, tem a ver com o StreamPosition
.Aqui está uma implementação que transmite o conteúdo do arquivo sem armazená-lo em buffer (buffer em byte [] / MemoryStream, etc. pode ser um problema no servidor, se for um arquivo grande).
Pode ser simplesmente usado assim:
fonte
var fs = new FileStream(FilePath, FileMode.Open, FileAccess.Read, FileShare.None, 4096, FileOptions.DeleteOnClose);
vez deFile.OpenRead(FilePath)
Não sei exatamente qual parte culpar, mas eis o porquê
MemoryStream
que não funciona para você:Enquanto você escreve
MemoryStream
, ele aumenta suaPosition
propriedade. O construtor deStreamContent
leva em consideração a corrente do fluxoPosition
. Portanto, se você gravar no fluxo e passá-lo paraStreamContent
, a resposta começará do nada no final do fluxo.Há duas maneiras de corrigir corretamente isso:
1) construir conteúdo, escrever para transmitir
2) escreva para transmitir, redefinir posição, construir conteúdo
2) parecer um pouco melhor se você tiver um fluxo novo, 1) será mais simples se o fluxo não iniciar em 0
fonte
Para mim, foi a diferença entre
e
O primeiro retornava a representação JSON de StringContent: {"Headers": [{"Key": "Content-Type", "Value": ["application / octet-stream; charset = utf-8"]}]}
Enquanto o segundo estava retornando o arquivo corretamente.
Parece que Request.CreateResponse tem uma sobrecarga que recebe uma string como o segundo parâmetro e parece ter sido o que estava fazendo com que o próprio objeto StringContent fosse renderizado como uma string, em vez do conteúdo real.
fonte