Как создать PDF-файл из бинарной строки, полученной от веб-сервиса, с использованием JavaScript
Описание проблемы
Я пытаюсь создать PDF-файл из бинарного потока, который получаю в ответ на AJAX-запрос.
С помощью XmlHttpRequest
я получаю следующие данные:
%PDF-1.4....
.....
....все данные, представляющие файл
....
%% EOF
Пока что я пробовал встроить свои данные через data:uri
. Это работает нормально и без ошибок. К сожалению, это не срабатывает в IE9 и Firefox. Возможно, причина в том, что у этих браузеров есть проблемы с использованием data-uri
.
Теперь я ищу какое-либо решение, которое будет работать во всех браузерах. Вот мой код:
// кодировка responseText
pdfText = $.base64.decode($.trim(pdfText));
// Теперь pdfText содержит %PDF-1.4 ...... данные ...... %%EOF
var winlogicalname = "detailPDF";
var winparams = 'dependent=yes,locationbar=no,scrollbars=yes,menubar=yes,'+
'resizable,screenX=50,screenY=50,width=850,height=1050';
var htmlText = '<embed width=100% height=100%'
+ ' type="application/pdf"'
+ ' src="data:application/pdf,'
+ escape(pdfText)
+ '"></embed>';
// Открыть PDF в новом окне браузера
var detailWindow = window.open ("", winlogicalname, winparams);
detailWindow.document.write(htmlText);
detailWindow.document.close();
Как я уже говорил, это работает хорошо в Opera и Chrome (Safari не тестировался). Используя IE или Firefox, открывается пустое новое окно.
Есть ли какое-либо решение, позволяющее создать PDF-файл в файловой системе для последующей загрузки пользователем? Мне нужно решение, которое будет работать во всех браузерах, по крайней мере в IE, FF, Opera, Chrome и Safari.
Я не имею прав на редактирование реализации веб-сервиса, поэтому решение должно быть клиентским. Есть идеи?
5 ответ(ов)
Чтобы создать PDF-файл на файловой системе и предоставить пользователю возможность его скачать, вы можете использовать следующий подход с помощью XMLHttpRequest
. Установите responseType
в blob
и используйте атрибут download
в элементе <a>
, чтобы осуществить скачивание PDF-файла. Ниже представлен пример кода, который вы можете использовать:
var request = new XMLHttpRequest();
request.open("GET", "/path/to/pdf", true);
request.responseType = "blob";
request.onload = function (e) {
if (this.status === 200) {
// Получен `blob`
console.log(this.response);
// Создание `objectURL` из полученного `blob` (.pdf)
var file = window.URL.createObjectURL(this.response);
var a = document.createElement("a");
a.href = file;
a.download = this.response.name || "detailPDF"; // Установка имени файла
document.body.appendChild(a);
a.click(); // Инициация скачивания
// Удаление элемента `a` после диалога сохранения,
// чтобы `window` снова получил фокус
window.onfocus = function () {
document.body.removeChild(a);
}
}
};
request.send();
Эта конструкция создаёт запрос к вашему серверу, получает PDF-файл в формате blob
и инициирует его скачивание. Не забудьте заменить "/path/to/pdf"
на действительный путь к вашему PDF-файлу.
Я понимаю, что это довольно старая тема, но вот решение, которое я разработал сегодня:
doSomethingToRequestData().then(function(downloadedFile) {
// создаем элемент ссылки для скачивания
var downloadLink = document.createElement('a');
downloadLink.target = '_blank';
downloadLink.download = 'имя_для_сохраненного_файла.pdf';
// конвертируем загруженные данные в Blob
var blob = new Blob([downloadedFile.data], { type: 'application/pdf' });
// создаем объектный URL из Blob
var URL = window.URL || window.webkitURL;
var downloadUrl = URL.createObjectURL(blob);
// задаем объектный URL как href ссылки
downloadLink.href = downloadUrl;
// добавляем ссылку в тело документа
document.body.append(downloadLink);
// инициируем клик по ссылке
downloadLink.click();
// очистка: удаляем элемент и отзываем объектный URL
document.body.removeChild(downloadLink);
URL.revokeObjectURL(downloadUrl);
}
Если у вас возникнут вопросы или потребуется дополнительная помощь, не стесняйтесь спрашивать!
Вы изменили код следующим образом:
var htmlText = '<embed width=100% height=100%'
+ ' type="application/pdf"'
+ ' src="data:application/pdf,'
+ escape(pdfText)
+ '"></embed>';
на
var htmlText = '<embed width=100% height=100%'
+ ' type="application/pdf"'
+ ' src="data:application/pdf;base64,'
+ escape(pdfText)
+ '"></embed>';
и это сработало для вас.
Вероятно, проблема заключалась в том, что вы не указывали правильный формат данных в строке base64. Когда вы добавили base64
в строку src
, браузер правильно распознал кодировку данных как base64, что и привело к успешному отображению PDF. Убедитесь, что pdfText
действительно содержит корректные данные в формате base64 для PDF, чтобы избежать возможных ошибок.
Ответ @alexandre с использованием base64 решает задачу.
Объяснение, почему это работает для IE, находится здесь:
https://en.m.wikipedia.org/wiki/Data_URI_scheme
В разделе 'формат', где указано:
Некоторые браузеры (Chrome, Opera, Safari, Firefox) принимают нестандартный порядок, если указаны оба параметра ;base64 и ;charset, в то время как Internet Explorer требует, чтобы спецификация charset предшествовала токену base64.
Ваш код на PHP для декодирования бинарных данных и отображения PDF-файла выглядит в целом правильно, но я хотел бы предложить несколько улучшений, чтобы убедиться, что все работает корректно.
Убедитесь, что данные, которые вы декодируете, действительно являются корректными данными в формате base64. Если они испорчены или неполные, это может привести к ошибке в отображении PDF.
В строке с
Content-Length:
можно добавить пробел послеContent-Length:
, иначе это может вызвать некоторые проблемы с заголовками.Проверьте, чтобы код выше не выводил никаких других текстов или пустых строк до отправки данных. Любые пробелы или строки могут испортить ответы HTTP.
Ниже приведен улучшенный вариант вашего кода:
<?php
$data = 'dfjhdfjhdfjhdfjhjhdfjhdfjhdfjhdfdfjhdf==blah...blah...blah..';
// Декодирование данных
$data = base64_decode($data);
// Проверка на успешное декодирование
if ($data === false) {
die('Ошибка декодирования данных.');
}
// Отправка заголовков для PDF
header("Content-Type: application/pdf");
header("Content-Length: " . strlen($data));
header("Content-Disposition: inline; filename=label.pdf");
// Вывод данных
echo $data;
exit;
?>
Обратите внимание на следующие моменты:
- Я добавил проверку на успешное декодирование данных — если данные не могут быть декодированы, код завершится с соответствующим сообщением об ошибке.
- Вместо
print
использованecho
, но это не критично — оба варианта работают. - Убедитесь, что до вызова
header
никаких данных не было выведено.
Попробуйте этот код, и если проблема сохраняется, проверьте, какие данные вы получаете от сервера для декодирования.
Как перенаправить на другую веб-страницу?
Прокрутка к элементу с использованием jQuery
Как определить нажатие клавиши Esc?
Удалить ВСЕ пробелы из текста
Как получить данные формы с помощью JavaScript/jQuery?