Angular / Web API 2使用StreamContent或ByteArrayContent返回无效或损坏的文件

时间:2016-06-14 21:44:00

标签: c# angularjs asp.net-web-api asp.net-web-api2 memorystream

我试图在ASP.NET Web API Controller中返回一个文件。此文件是保存在MemoryStream中的动态生成的PDF。

客户端(浏览器)成功接收文件,但是当我打开文件时,我发现所有页面都是空白的。

问题是,如果我使用相同的MemoryStream并将其写入文件,则此磁盘文件会正确显示,因此我认为该问题与通过Web传输文件有关。

我的控制器看起来像这样:

[HttpGet][Route("export/pdf")]
public HttpResponseMessage ExportAsPdf()
{
    MemoryStream memStream = new MemoryStream();
    PdfExporter.Instance.Generate(memStream);

    memStream.Position = 0;
    HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);
    result.Content = new ByteArrayContent(memStream.ToArray()); //OR: new StreamContent(memStream);
    return result;
}

试试,如果我将流写入磁盘,它会正确显示:

[HttpGet][Route("export/pdf")]
public HttpResponseMessage ExportAsPdf()
{
    MemoryStream memStream = new MemoryStream();
    PdfExporter.Instance.Generate(memStream);

    memStream.Position = 0;
    using (var fs = new FileStream("C:\\Temp\\test.pdf", FileMode.OpenOrCreate, FileAccess.ReadWrite))
    {
        memStream.CopyTo(fs);
    }

    return null;
}

区别在于:

  • PDF保存在磁盘上:34KB
  • 通过网络传输的PDF:60KB(!)

如果我比较两个文件内容,主要区别是:

File Differences

左边是通过网络传输的PDF;在右侧,PDF保存到磁盘。

我的代码有问题吗? 也许与编码有关的东西?

谢谢!

3 个答案:

答案 0 :(得分:4)

嗯,原来是客户端(浏览器)问题,而不是服务器问题。我在前端使用AngularJS,因此当收到respose时,Angular会自动将其转换为Javascript字符串。在该转换中,文件的二进制内容以某种方式被改变了......

基本上它是通过告诉Angular不将响应转换为字符串来解决的:

$http.get(url, { responseType: 'arraybuffer' })
.then(function(response) {
    var dataBlob = new Blob([response.data], { type: 'application/pdf'});
    FileSaver.saveAs(dataBlob, 'myFile.pdf');
});

然后在Angular File Saver服务的帮助下将响应保存为文件。

答案 1 :(得分:3)

我猜您应该像这样设置ContentDispositionContentType

[HttpGet][Route("export/pdf")]
public HttpResponseMessage ExportAsPdf()
{
    MemoryStream memStream = new MemoryStream();
    PdfExporter.Instance.Generate(memStream);

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

答案 2 :(得分:0)

试试这个

[HttpGet][Route("export/pdf")]
public HttpResponseMessage ExportAsPdf()
{
    MemoryStream memStream = new MemoryStream();
    PdfExporter.Instance.Generate(memStream);

    //get buffer
    var buffer = memStream.GetBuffer();
    //content length for header
    var contentLength = buffer.Length;

    var statuscode = HttpStatusCode.OK;
    var response = Request.CreateResponse(statuscode);
    response.Content = new StreamContent(new MemoryStream(buffer));
    response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
    response.Content.Headers.ContentLength = contentLength;
    ContentDispositionHeaderValue contentDisposition = null;
    if (ContentDispositionHeaderValue.TryParse("inline; filename=my_filename.pdf", out contentDisposition)) {
        response.Content.Headers.ContentDisposition = contentDisposition;
    }

    return response;
}