Yêu cầu thg 8 26, 2020 2:15 CH 2812 2 2
  • 2812 2 2
+1

Cách xử lý download file sử dụng Ajax - Javascript

Chia sẻ
  • 2812 2 2

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


Đã trả lời thg 8 27, 2020 10:51 SA
Đã được chấp nhận
+2

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.

Chia sẻ
Avatar Ngân Kim @ngankim
thg 8 27, 2020 1:00 CH

Cảm ơn bác nhiều à, code này quá ngon rồi à 💯🙏

Đã trả lời thg 8 27, 2020 1:06 SA
+1

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

Chia sẻ
Avatar Ngân Kim @ngankim
thg 8 27, 2020 5:43 SA

@hungpv E cũng đọc và thử qua cách bác đề cập rồi, mà vẫn ko đọc đc.
Hay do code Ajax của em có vấn đề gì nhỉ ?

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí