2020-02-11
zelder
2020-02-11
11/02
2020

Запрос PDF-файла AJAX (Post) запросом.

Задача: на странице сайта есть форма с параметрами от клиента. На основе них сгенерировать PDF-файл на стороне сервера и вернуть как файл.
В этой статье пример реализации второй части задачи (возврат PDF-файла).

Данное решение не ограничивается только PDF файлами.
Этот формат как для примера.


Серверная сторона (C#) (для краткости примера без всяких try и проверок):
var stream = // поток данных с файлом (не обязательно PDF)
var fileName = "Название файла.pdf"; // если на русском, то кодировать будем
// to base64
string dataBase64;
byte[] buffer = new byte[stream.Length];
stream.Read(buffer, 0, (int)stream.Length);
dataBase64 = Convert.ToBase64String(buffer);
stream.Close();
stream.Dispose();
// отдаем
this.Response.Headers["Content-Disposition"] = $"attachment; filename=\"{Uri.EscapeUriString(fileName)}\""; // Uri.EscapeDataString(fileName)
this.Response.Headers["Content-Type"] = "application/pdf";
return Content(dataBase64);


Клиентская сторона (javascript):
// $btn - что-то от JQ, куда будет вставлен текстовый ответ (для этого примера)

$.ajax({
            url: 'some_url_to_server',
            data: {p1: 'v1', p2: 'v2'},
            method: "POST",
            cache: false,
            responseType: 'arraybuffer',
            success: function (data, status, xhr) {
                var type = xhr.getResponseHeader("Content-Type");
                // error (договоренность, если текст - значит ошибка)
                if (type === 'text/html') {
                    $btn.replaceWith("<div style='color:#F00; font-weigth: bold; padding:10px;'>" + data + "</div>");
                    return;
                }
                // разбор названия файла (из заголовка ответа)
                var filename = "Заявление.pdf";
                var disposition = xhr.getResponseHeader('Content-Disposition');
                if (disposition) {
                    var filenameRegex = /filename[^;=\n]*=((['"]).*?|[^;\n]*)/;
                    var matches = filenameRegex.exec(disposition);
                    if (matches !== null && matches[1]) {
                        var fnstr = matches[1].replace(/['"]/g, '');
                        if (typeof (decodeURI) !== 'undefined') filename = decodeURI(fnstr); // на сервере было: Uri.EscapeUriString
                    }
                } 
// пытаемся загрузить браузером полученный файл
                try {
                    var blob = b64toBlob(data, type); // // конвертирует base64 строку к объект Blob (реализация метода ниже)
// for MS
                    if (typeof window.navigator.msSaveBlob !== 'undefined') {
                        window.navigator.msSaveBlob(blob, filename);
                    }
                    else {
                        var URL = window.URL || window.webkitURL;
                        var downloadUrl = URL.createObjectURL(blob);
                        var a = document.createElement('a');
                        // Safari doesn"t support this yet.
                        if (typeof a.download === "undefined") {
                            window.location = downloadUrl;
                        } else {
                            a.href = downloadUrl;
                            a.download = filename;
                            document.body.appendChild(a);
                            a.click();
                        }
                    }
                    $btn.show();
                }
                catch (ex) {
                    $btn.replaceWith("<div>" + "Ошибка выполнения" + "</div>");
                }
            },
            error: function (x, t, e) {
                $btn.replaceWith("<div>" + e + "</div>");
            }
        });

Метод конвертации (64toBlob):
// конвертирует base64 строку к объект Blob
if (!b64toBlob) {
    b64toBlob = function (content, contentType) {
        contentType = contentType || '';
        const sliceSize = 512;
        // method which converts base64 to binary
        const byteCharacters = window.atob(content);
        const byteArrays = [];
        for (let offset = 0; offset < byteCharacters.length; offset += sliceSize) {
            const slice = byteCharacters.slice(offset, offset + sliceSize);
            const byteNumbers = new Array(slice.length);
            for (let i = 0; i < slice.length; i++) {
                byteNumbers[i] = slice.charCodeAt(i);
            }
            const byteArray = new Uint8Array(byteNumbers);
            byteArrays.push(byteArray);
        }
        const blob = new Blob(byteArrays, {
            type: contentType
        }); // statement which creates the blob
        return blob;
    };
};



.