2013年11月29日 星期五

【Javascript】XMLHttpRequest 解決 Cross Domain 檔案下載

需求為 Frontend.js 與 實際檔案放置在不同主機上,
所以需要解決 Cross Domain 資料存取的限制。

解決辦法為採用 XMLHttpRequest 物件來突破跨區存取限制。
直接看 Code
downloadFile = function( _fileUrl ) {
    var xhr = new XMLHttpRequest();

    if ("withCredentials" in xhr) {
        xhr.open('GET', _fileUrl, true);
    }    
    else if (typeof XDomainRequest != "undefined") {
        xhr = new XDomainRequest();
        xhr.open('GET', _fileUrl );
    }
        
    xhr.responseType = "blob";
    xhr.onreadystatechange = function () { 
        if (xhr.readyState == 4) {
            var a = document.createElement('a');
            a.href = window.URL.createObjectURL(xhr.response);
            a.download = fileName;
            a.click();
        }
    };

    xhr.send();
}

downloadFile 這個函式接收一個參數 _fileUrl 指出檔案路徑
var xhr = new XMLHttpRequest() 建立一個 xhr 物件
withCredentials這個屬性預設為false,
因為 XMLHTTPRequest2 物件才允許CORS(Cross-Origin Resource Sharing),
所以檢查是否有支援withCredentials屬性,
該屬性指示是否使用如cookie或授權標頭檔等憑證進行跨站存取控制(cross-site Access-Control)請求。

而IE 8與IE 9只能使用 XDomainRequest 物件處理 CORS 請求(IE8,9不支援XHR2)
responseType 屬性指定回應狀態,
可以為 blob, json, text 等等,
為了方便後續創造連結,所以指定回應狀態為"blob"
onreadystatechange 這個函式會在 readyState 屬性改變時被呼叫
而 readyState 屬性值為 4 時,表示作業完成。
此時便可以創造一個元素 a,並且呼叫 createObjectURL 方法賦予 href 屬性值,
a.download可以為下載檔案命名,最後呼叫 click 方法完成檔案下載。

還有一件事情須處理,存放檔案的主機要設定允許跨領域存取的權限
後端採用 Node.js 與 Express framework 進行開發
app.configure(function(){ 
  app.use(function(req, res, next) {
      res.header('Access-Control-Allow-Origin', "*");
      res.header('Access-Control-Allow-Credentials', true);
      res.header('Access-Control-Allow-Methods', "GET");
      next();
  });
});

簡單講要幫 response 設定允許跨領域存取的 Header,
'Access-Control-Allow-Origin' 設定允許的請求來源
'Access-Control-Allow-Methods'設定允許的方法,如 GET, POST 等
如果前端 xhr.setRequestHeader 有自行定義 Header 的話
那 Server 這邊也要多加 'Access-Control-Allow-Headers' 權限
res.header('Access-Control-Allow-Headers', 'X-Requested-With');

就允許帶有 header 為 'X-Requested-With' 的請求。
但要注意的是,因為安全性的關係,XDomainRequest不支援客製化 Header
也限制 Method only GET or POST,Protocol only HTTP or HTTPS
還好IE 10開始支援 XMLHTTPRequest2

沒有留言:

張貼留言