Cách xử lý download file sử dụng Ajax - Javascript
Mọi người cho em hỏi về xử lý download file sử dụng javascript với ạ.
Vấn đề của em là:
Ở màn hình có nút download csv.
Khi user click download button thì sẽ dùng ajax, call api để download CSV về.
Api sẽ làm check :
- Trường hợp 1: Nếu không có vấn đề gì thì sẽ tạo csv và response file csv đó về cho user.
- Trường hợp 2: Nếu có vấn đề thì trả về message cảnh báo cho user.
Trường hợp 1 em đã download file ok rồi.
Vấn đề của em là ở trường hợp 2, e ko thể lấy được message cảnh báo khi server trả về, để show cho user.
Lý do là để download e đang khai báo responseType: 'blob' trong ajax, nên responseType luôn ở dạng blob và ** em ko lấy được message đó ra**.
Mặc dù xem trong response thì vẫn thấy message.
Nên em đang đành phải để 1 cái message chung chung là "có lỗi sảy ra..."
Câu hỏi của em là có cách nào lấy được error message từ server trả về khi responseType của Ajax để là blob ko.
Câu hỏi ngắn, nhưng giải thích hơi lan man, cảm ơn mọi người đã đọc tới đây ạ.
Rất mong nhận được sự giúp đỡ của các tiền bối.
Đây là code PHP của em.
try{
// Thực hiện tạo csvs và zip
throw UserOfException('Lý do 1');
...........................
throw UserOfException('Lý do N');
............................
//..........................
return response()->download($zip);
} catch(UserOfException $e) {
cache()->put('downloadCsvSet_using_person', 0);
Log::warning($e->getMessage());
return response()->json(['message' => $e->getMessage()], 404);
}
Code JS:
$.ajax({
url: $('#form').attr('action'),
method: 'POST',
data: {
invoice_ids: $('#invoice_ids').val(),
},
xhrFields: {
responseType: '**blob**',
},
success: function (data, status, xhr) {
let filename = "";
let disposition = xhr.getResponseHeader('Content-Disposition');
if (disposition && disposition.indexOf('attachment') !== -1) {
let filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
let matches = filenameRegex.exec(disposition);
if (matches != null && matches[1]) filename = matches[1].replace(/['"]/g, '');
}
let a = document.createElement('a');
let url = window.URL.createObjectURL(data);
a.href = url;
a.download = filename.replace('UTF-8', '');;
document.body.append(a);
a.click();
a.remove();
window.URL.revokeObjectURL(url);
$("#overlay").fadeOut(300);
},
error: function (xhr) {
// console.log(xhr.responseText); -> output is blob
$("#overlay").fadeOut(300);
$.notify("Có lỗi sảy ra", 'error');
}
});
2 CÂU TRẢ LỜI
Anh nghĩ em ko thể convert đc khi server trả về khi có error là json mà ở trong ajax em lại để blob.
Code em nên sửa lại như sau.
Phía server em có thể trả về plain/text nếu thực sự em chỉ có mỗi message trả về.
Đoạn return
return response($e->getMessage(), 404)->header('Content-Type', 'text/plain');
Còn phía Javascript em nên if else để set responseType thay vì em đang fix cứng
Code JS
$.ajax({
url: $('#form').attr('action'),
method: 'POST',
data: {
invoice_ids: $('#invoice_ids').val(),
},
//xhrFields: {
//responseType: '**blob**',
//},
// sửa thành
xhr: function () {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
if (xhr.readyState == 2) {
if (xhr.status == 200) {
xhr.responseType = "blob";
} else {
xhr.responseType = "text";
}
}
};
return xhr;
},
success: function (data, status, xhr) {
let filename = "";
let disposition = xhr.getResponseHeader('Content-Disposition');
if (disposition && disposition.indexOf('attachment') !== -1) {
let filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
let matches = filenameRegex.exec(disposition);
if (matches != null && matches[1]) filename = matches[1].replace(/['"]/g, '');
}
let a = document.createElement('a');
let url = window.URL.createObjectURL(data);
a.href = url;
a.download = filename.replace('UTF-8', '');;
document.body.append(a);
a.click();
a.remove();
window.URL.revokeObjectURL(url);
$("#overlay").fadeOut(300);
},
error: function (xhr) {
// console.log(xhr.responseText); -> output is blob
$("#overlay").fadeOut(300);
$.notify("Có lỗi sảy ra", 'error');
}
});
Chắc sẽ có nhiều cách hay hơn đó.
Lót dép chờ PRO của Sun* trả lời thêm nhé em.
Cảm ơn bác nhiều à, code này quá ngon rồi à
lấy dữ liệu từ blob bằng FileReader, đoạn sau đọc dữ liệu từ blob thành array
const reader = new FileReader();
reader.addEventListener('loadend', () => {
// reader.result contains the contents of blob as a typed array
});
reader.readAsArrayBuffer(blob);
cách khác là dùng Response, đoạn dưới là đọc thành text
const text = await (new Response(blob)).text();
hoặc là dùng Blob.prototype.text():
const text = await blob.text();
Dùng các cách ngoài FileReader thì có thể lấy được nội dung của blob dưới dạng string hoặc dữ liệu URL.
nguồn: https://developer.mozilla.org/en-US/docs/Web/API/Blob