简体   繁体   English

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

[英]Angular / Asp.Net Web Api request binary data

I have an Angular 4 application which consumes an Asp.Net Web Api, and I want the Api to return a binary file. 我有一个使用Asp.Net Web Api的Angular 4应用程序,我希望Api返回一个二进制文件。 The Api route seems to be working correctly - I tested it using a rest console and the response is as expected. Api路由似乎正常工作-我使用了一个rest控制台对其进行了测试,并且响应符合预期。 However, when trying to use the same route in the Angular app, the request sends but returns an error. 但是,当尝试在Angular应用中使用相同的路由时,请求发送但返回错误。 I can see with the C# debugger that the request is executing completely and doesn't fail on the server. 我可以使用C#调试器看到请求已完全执行,并且在服务器上没有失败。 Here's the error in the JS console: 这是JS控制台中的错误: 在此处输入图片说明

This error occurs on all browsers tested (Chrome, Firefox, IE, Edge, Safari). 在所有经过测试的浏览器(Chrome,Firefox,IE,Edge,Safari)上都会发生此错误。

Here's the server side code: 这是服务器端代码:

[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);
}

And here's the Angular code: 这是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));

EDIT: I tried using a plain XMLHttpRequest instead of Angular's Http service and it works. 编辑:我尝试使用普通的XMLHttpRequest而不是Angular的Http服务,并且它可以工作。 What do I need to do to get this to work with Angular? 我需要做什么才能使其与Angular一起使用?

EDIT 2: It works if I fetch an actual file on the file system that's accessible using the same host that the Angular app is running on. 编辑2:如果我在使用Angular应用程序运行所在的同一主机可访问的文件系统上获取实际文件,则该方法有效。 The Angular app is on localhost:8080, while the api is on a different port. Angular应用位于localhost:8080上,而api位于其他端口上。 If I expose a file on localhost:8080 (eg, in the build folder) than I can fetch that file. 如果我在localhost:8080上公开一个文件(例如,在构建文件夹中),则可以获取该文件。 This makes me wonder if it's a security issue, or maybe has to do with the headers or the way Web Api returns the binary data. 这使我想知道这是一个安全问题,还是与标头或Web Api返回二进制数据的方式有关。

On your Api that will return your PDF 在您的Api上将返回您的PDF

  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");
  }

And then on Angular: 然后在Angular上:

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

Try the old Way: 尝试旧方法:

            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()

This is for an image that I was keeping in a blob column in a db, but process should be similar. 这是针对我保留在db的blob列中的图像,但是过程应该相似。 I ended up doing something like this back in Angular 4 (although this was 4.3+ which means HttpClient, not Http) to handle downloading files on clicking a button: 我最终在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); } }) } 

This API is .Net Core, but should be similar in .Net MVC, I believe: 该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);
  }
}

The second answer by Crying Freeman, using Response directly, does work, but it bypasses Owin's processing and would mean having to manually implement things like CORS or anything else normally handled using CORS. 直接使用Response的Crying Freeman的第二个答案确实有效,但是绕过了Owin的处理,这意味着必须手动实现CORS之类的东西或其他通常使用CORS处理的东西。

I found another solution , to use a custom formatter to allow returning a byte array from the controller method. 我找到了另一个解决方案 ,使用自定义格式器允许从controller方法返回字节数组。 This is also nicer because I don't need to set any headers manually, not even Content-Type. 这也更好,因为我不需要手动设置任何标题,甚至不需要Content-Type。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM