以下是程式碼:
postvalue = {};
request = $.ajax({
url: "apiUrl",
type: "post",
data: JSON.stringify(postvalue),
contentType: "application/json; charset=utf-8",
xhrFields: {
responseType: 'blob'
},
success: function (blob, status, xhr) {
//check for a filename
var disposition = xhr.getResponseHeader('Content-Disposition');
if (disposition && disposition.indexOf('attachment') !== -1) {
disposition = disposition.split("'");
}
filename = decodeURIComponent(disposition[2]);
if (typeof window.navigator.msSaveBlob !== 'undefined') {
window.navigator.msSaveBlob(blob, filename);
} else {
var URL = window.URL || window.webkitURL;
var downloadUrl = URL.createObjectURL(blob);
if (filename) {
// use HTML5 a[download] attribute to specify filename
var a = document.createElement("a");
// safari doesn't support this yet
if (typeof a.download === 'undefined') {
window.location.href = downloadUrl;
} else {
a.href = downloadUrl;
a.download = filename;
document.body.appendChild(a);
a.click();
}
} else {
window.location.href = downloadUrl;
}
}
},
error: function (result) {
console.log(result.responseText);
},
});
在上述程式碼中,設定了
xhrFields: {
responseType: 'blob'
},
成功success(Status Code: 200) API 會回傳 blob 資料,接著進行處理取得檔名和下載的動作,這部分沒有問題。
問題出在如果失敗error(Status Code: 401) API 會回傳 json 資料,透過瀏覽器開發工具 Fetch/XHR 查看該 API 的 preview 和 respones 內容都可以看到正確的 json/字串值,例如: {error:"錯誤"},
但是 console.log(result.responseText); 輸出卻是 "[object Blob]" 這樣的字串值,
(且用 console.log(typeof result.responseText); 檢查,輸出的結果是 string 不是 blob)
如果我把設定:responseType: 'blob' 移除,
error 的 console.log(result.responseText); 輸出就是正確的字串值({error:"錯誤"})
但是 success 裡面這行 var downloadUrl = URL.createObjectURL(blob); 就會出現錯誤:Uncaught TypeError: Failed to execute 'createObjectURL' on 'URL': Overload resolution failed.
請問有沒有辦法可以個別設定 success / error 回傳的資料格式為 blob / json?
回傳一律用json,succeed回傳
{ result : "ok" , code: 0 , data: blob }
error 則
{ result: "error" , code : 999 , data : null }
收到json回傳,再用javascript 判斷code 多少再看要不要把data讀出來
謝謝,你的方式是可行的,可以把blob資料以base64的方式回傳於json內容中,
以下提供修改方式做參考:
// 原本的程式碼這一段不需要了,filename也可以直接回傳於json:
// check for a filename
var disposition = xhr.getResponseHeader('Content-Disposition');
if (disposition && disposition.indexOf('attachment') !== -1) {
disposition = disposition.split("'");
}
filename = decodeURIComponent(disposition[2]);
改成
// 將base64轉換成Blob
const byteChars = atob(JsonBase64Contents);
const byteNumbers = new Array(byteChars.length);
for (let i = 0; i < byteChars.length; i++) {
byteNumbers[i] = byteChars.charCodeAt(i);
}
const byteArr = new Uint8Array(byteNumbers);
const blob = new Blob([byteArr], { type: 'application/vnd.ms-excel' });
filename = JsonFilename;
另一種消極的做法就是,如果error的話
就用 responseType: 'json' 再 post 一次,取得可解析的回傳值
如果因為某些緣故無法改後端的 API
直接用 fetch 也不難
fetch('apiUrl', {
method: 'post',
headers: {
'Content-Type': 'application/json; charset=utf-8'
},
body: JSON.stringify(postvalue)
}).then(r => {
return new Promise((resolve, reject) => {
if (r.status === 200) {
r.blob().then(x => resolve(x));
} else {
r.json().then(x => reject(x));
}
})
}).then(blob => {
console.log('success', blob);
}, json => {
console.log('error', json);
});
非常感謝指導!這個做法確實能解決 API 無法修改的困境!
自己幫自己補充 axios 的作法:
要先載入 axios 套件
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
axios({
method: 'post',
url: 'apiurl',
data: postvalue,
responseType: 'blob',
headers: {
'Content-Type': 'application/json; charset=utf-8'
}
}).then(response => {
let disposition = response.headers['content-disposition'];
let filename = 'unknown';
if (disposition) {
//check for a filename
if (disposition && disposition.indexOf('attachment') !== -1) {
disposition = disposition.split("'");
}
filename = decodeURIComponent(disposition[2]);
}
const blob = new Blob([response.data], { type: response.headers['content-type'] });
console.log('success', filename, blob);
//處理檔案下載的程式碼片段(省略)
}).catch(error => {
$("#loading").hide();
//console.log('error', error);
let reader = new FileReader();
reader.addEventListener("loadend", function () {
let text = reader.result;
let errorJSON = JSON.parse(text);
console.log(errorJSON);
});
reader.readAsText(error.response.data);
});