javascript - 透過 Angular $http 而不是 XMLHttpRequest 呈現 PDF
所以我花了好幾週的時間研究如何通過 ajax 從伺服器獲取 PDF,然後顯示給客戶端。我需要這麼做的原因是使用者不能直接訪問伺服器上的 PDF,因為他們不會提供用於通過安全驗證的令牌,但使用 JavaScript 我可以做到。使用以下代碼,我能夠獲得我所期望的結果:
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.responseType = 'arraybuffer';
xhr.setRequestHeader("token", token.value);
xhr.onload = function (e) {
if (this.status == 200) {
var file = new Blob([this.response], { type: 'application/pdf' });
var fileURL = URL.createObjectURL(file);
window.open(fileURL, '_blank');
}
};
xhr.send();
然而,當我嘗試在 Angular 中像這樣做完全相同的事情時(我只是做其中一半的東西,這樣兩邊的標頭才匹配):
var config = {
url: url,
method: 'GET',
responseType: 'arraybuffer',
headers: {
'Accept':'*/*',
"token": token.value
}
}
$http(config)
.success(function (response) {
var file = new Blob([response], { type: 'application/pdf' });
var fileURL = URL.createObjectURL(file);
window.open(fileURL, '_blank')
});
這是在兩者中的標題樣式:
GET http://example.com HTTP/1.1
Host: example.com
Connection: keep-alive
Accept: */*
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.94 Safari/537.36
token: test
Referer: http://example.com
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US,en;q=0.8
唯一的區別是 xmlhttprequest 版本有 Authorization: Negotiate 標頭,但我可以在 Fiddler 中根據伺服器的回應渲染它們。這告訴我,$http 的 'response' 與 xmlhttprequest 中的 this.response 並不相同。有人可以告訴我差異在哪裡,以及我該如何修復它嗎?
我曾長時間以為這是字元集的問題,我甚至在傳輸前對位元組進行了 base64 編碼,然後使用 atob() 將其轉回,但這似乎並沒有改善情況。看起來 $http 版本會將回應的位元組轉成 utf8,即使它們並不是…我想我的位元組是 windows-1252/latin1/ios-8895-1。
我之所以這麼說,是因為我在十六進位編輯器中看到了這個:
btye 85(在伺服器上)切換為兩個位元組的 C2 85(在 $http 代碼之後)http://www.fileformat.info/info/unicode/char/85/charset_support.htm
btye c5(在伺服器上)切換為兩個位元組 c3 85(在 $http 代碼之後)http://www.fileformat.info/info/unicode/char/c5/charset_support.htm
我甚至嘗試過對各種組合進行編碼/解碼,但都無濟於事。我真的認為當 PDF 通過 Angular 時,它以某種方式被損壞了,但我不知道是如何發生的。
更新 1
在伺服器端,程式碼看起來像這樣:
public class LabelController : Controller
{
//using Microsoft.AspNet.Mvc
[HttpGet, Route("api/labels/box")]
public IActionResult BoxLabels(Guid requestId)
{
var data = this.getPDF("http://foobar.com/example.pdf");
Response.Headers.Add("Content-Length", data.Length.ToString());
Response.Headers.Add("Content-Disposition", "inline; filename=example.pdf");
return new FileContentResult(data, "application/pdf");
}
//using restsharp
private bytes[] getPDF(url)
{
var client = new RestClient
{
BaseUrl = new Uri(url),
Authenticator = new NtlmAuthenticator()
};
var request = new RestRequest
{
Method = Method.POST,
Credentials = CredentialCache.DefaultNetworkCredentials,
RequestFormat = DataFormat.Xml
};
var response = client.Execute(request);
return response.RawBytes;
}
}
這大致看起來是這樣:http://plnkr.co/edit/R3FiesPk7Os22SMsTTfE,沒有「No 'Access-Control-Allow-Origin'」錯誤
更新 2
這是網路流量告訴我的情況:
使用 $http:時,PDF 長這樣
答案#1
所以我認為你的問題是瀏覽器正在進行 CORS 預檢,因為你正在進行跨瀏覽器請求。它將 GET 請求轉變為 OPTIONS。
您需要將 OPTIONS 請求新增到您接受的 CORS 選項中。您可能還需要將您的 IP 位址新增到 CORS 選項中接受的來源。
這裡有一些有用的連結:http://barnabas.tumblr.com/post/87914971548/the-curse-of-the-cors-preflight-and-how-to-defeat 對於如何處理 CORS OPTIONS 預檢請求感到困惑
答案#2
我正在嘗試學習 Angular,並且覺得看看這是否真的是 Angular 混淆問題會很有趣。所以我建立了一個簡單的 Node 網頁伺服器,並且使用了完全相同的你的程式碼。
我可以使用你說失敗的 Angular 代碼在瀏覽器窗口中顯示 PDF。唯一的問題是,你的方法在微軟的 Edge 瀏覽器中無法使用,因為出於安全原因不支援 blob URL。
程式碼在 GitHub 上,早期版本在 C9 上。
然後我想知道這是否是 IIS/restsharp/ASP.NET MVC 的問題。然而,我很快就使用 restsharp 創建了一個 ASP.NET MVC 應用程序,這個應用程序也可以與 Angular 配合使用,代碼與你的非常相似。這個項目也在 GitHub 上。你的 restsharp 代碼需要一些調整,但對我來說第一次使用就完全正常工作了。
所以你在這裡沒有得到任何答案的原因是,我認為問題中的資訊不足以弄清你環境中出了什麼問題。不過我希望這些資料對你有幫助。


