Angular / Asp.Net Web Api请求二进制数据

时间:2018-07-19 21:22:29

标签: asp.net ajax angular asp.net-web-api xmlhttprequest

我有一个Angular 4应用程序,该应用程序使用Asp.Net Web Api,并且我希望Api返回一个二进制文件。 Api路由似乎正常工作-我使用了一个rest控制台对其进行了测试,并且响应符合预期。但是,当尝试在Angular应用中使用相同的路由时,请求发送但返回错误。我可以使用C#调试器看到请求已完全执行,并且在服务器上没有失败。这是JS控制台中的错误:enter image description here

在所有经过测试的浏览器(Chrome,Firefox,IE,Edge,Safari)上都会发生此错误。

这是服务器端代码:

[Route("api/getfile")]
public IHttpActionResult GetFile()
{
    byte[] file = <file generator code>;
    System.Net.Http.HttpResponseMessage responseMessage = new System.Net.Http.HttpResponseMessage
    {
        Content = new System.Net.Http.StreamContent(new System.IO.MemoryStream(file))
    };
    responseMessage.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/octet-stream");
    responseMessage.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");
    responseMessage.Content.Headers.ContentDisposition.FileName = "file.pdf";
    return this.ResponseMessage(responseMessage);
}

这是Angular代码:

let headers = new Headers({
  "Accept": "application/octet-stream",
  "X-Requested-With": "XMLHttpRequest",
  "Authorization": `Bearer ${token}`
});
let opts = new RequestOptions({headers = headers});
opts.responseType = ResponseContentType.Blob;
// Uses old @angular/http, not HttpClient
this.http
  .get(`${apiUrl}/getfile`, opts)
  .map(res => res.blob())
  .catch(err => handleError(err));

编辑:我尝试使用普通的XMLHttpRequest代替Angular的Http服务,并且它可以工作。我需要做什么才能使其与Angular一起使用?

编辑2:如果我在使用Angular应用程序运行所在的同一主机可访问的文件系统上获取实际文件,则该文件有效。 Angular应用位于localhost:8080上,而api位于其他端口上。如果我在localhost:8080上公开文件(例如,在构建文件夹中),则可以获取该文件。这使我想知道这是安全问题,还是与标头或Web Api返回二进制数据的方式有关。

3 个答案:

答案 0 :(得分:0)

在将返回PDF的Api上

  FileContentResult result;
  if(!string.IsNullOrEmpty(fileName))
  {
     string absoluteFileName = Path.Combine(pathToFile, fileName);
     byte[] fileContents = System.IO.File.ReadAllBytes(absoluteFileName);
     result = new FileContentResult(fileContents, "application/pdf");
  }

然后在Angular上

 downloadFile(api: string) {
    window.open(this.endPoint + api);
  }

尝试旧方法:

            FileInfo  fileInfo = New FileInfo(filePath)         
        Response.Clear()
        Response.ClearHeaders()
        Response.ClearContent()
        Response.AddHeader("Content-Disposition", "attachment;filename=" + fileInfo.Name)
        Response.AddHeader("Content-Type",  "application/pdf")
        Response.ContentType = "application/pdf"
        Response.AddHeader("Content-Length", fileInfo.Length.ToString())
        Response.TransmitFile(fileInfo.FullName)
        Response.Flush()
        Response.End()

答案 1 :(得分:0)

这是针对我保留在db的blob列中的图像,但是过程应该相似。我最终在Angular 4中做了类似的事情(尽管这是4.3+,这意味着HttpClient,而不是Http),以便单击按钮来处理文件下载:

public downloadImage(id: number, imageName: string, imageType: string) {

    this.http.get(urlToApiHere, { responseType: 'blob' }).subscribe((image: Blob) => {
      if (isPlatformBrowser(this.platformId)) {
        let a = window.document.createElement("a");
        document.body.appendChild(a);
        let blobUrl = window.URL.createObjectURL(image);
        a.href = blobUrl;
        a.download = imageName;
        a.click();
        window.URL.revokeObjectURL(blobUrl);
        document.body.removeChild(a);
      }
    })
  }

此API是.Net Core,但我认为在.Net MVC中应该类似:

[HttpGet]
public FileResult DisplayLineItemImage(int lineItemID)
{
  using (var db = new Context())
  {
    var image = //retrieve blob binary, type, and filename here
    if (image.Image == null)
    {
      //throw error
    }
    return File(image.Image, image.Type, image.Name);
  }
}

答案 2 :(得分:0)

Crying Freeman的第二个答案确实可以使用,但是直接绕过了Owin的处理,但这意味着必须手动实现诸如CORS之类的东西或其他通常使用CORS处理的事情。

我发现another solution使用一个自定义格式器来允许从controller方法返回字节数组。这样也更好,因为我不需要手动设置任何标题,甚至不需要Content-Type。