我有一个Angular 4应用程序,该应用程序使用Asp.Net Web Api,并且我希望Api返回一个二进制文件。 Api路由似乎正常工作-我使用了一个rest控制台对其进行了测试,并且响应符合预期。但是,当尝试在Angular应用中使用相同的路由时,请求发送但返回错误。我可以使用C#调试器看到请求已完全执行,并且在服务器上没有失败。这是JS控制台中的错误:
在所有经过测试的浏览器(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返回二进制数据的方式有关。
答案 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。