Рассматривается эффективный приём сетевой загрузки файлов, реализуемый на языке JavaScript (JS). Данный приём может быть актуален при создании как веб-ресурсов, так и десктопных приложений.
При создании страниц веб-сайтов нередко возникает потребность в предоставлении возможности скачивания файла для пользователя. Эта задача часто возникает при разработке ПО, и в большинстве случаев включает особые требования, в том числе анимированный процесс загрузки, отвечающий общему визуальному концепту разрабатываемого сайта. В большинстве случаев, при решении данной задачи используют два распространённых подхода:
- скачивание посредством использования HTML тэга <a>;
- асинхронное скачивание путём формирования AJAX запроса;
Однако два этих подхода будут по-разному функционировать в рамках исполнения JS кода браузером, особенно в условиях недостаточного объёма оперативной памяти компьютера.
Скачивание путём размещения ссылки на странице.
При использовании данного подхода на странице размещается тэг ссылка, ведущая к URL адресу для скачивания файла, например, следующим образом:
<a href="https://example.com/file.pdf" download> Download PDF </a>
Подобная ссылка будет открыта браузером ровно так же, как и совершается переход на новую странницу сайта. В случае исполнения традиционного перехода на новую страницу браузер получает заголовок Content-Type: text/html; charset=UTF-8, что указывает браузеру на то, что полезные данные, которые будут получены под данным заголовком, следует отобразить как HTML страницу. Но, в отличие от перехода на новую страницу сайта, при чтении заголовков ответа от сервера браузер получит следующие заголовки, диктующие особые инструкции по открытию данной ссылки:
Content-Type: application/pdf
Content-Disposition: attachment; filename="document.pdf"
Content-Length: 512000
В случае с заголовком Content-Type: application/pdf и двумя сопутствующими, браузер получает инструкцию что полезные данные в данной передаче необходимо воспринимать как файл, для последующего размещения браузером внутри файловой системы согласно настройкам браузера.
В некоторых случаях, для выполнения успешного скачивания на стороне сервера программистом формируются значения для данных заголовков, с последующей передачей в качестве ответа клиентской части в рамках организации HTTP взаимодействия. Чаще всего формирование заголовков требуется при использовании ссылки, явно не указывающей на расположение целевого файла на стороне сервера.
Преимущества и недостатки данного подхода:
Весомым преимуществом данного подхода является то, что процесс получения данных с клиентской стороны и индикация процесса загрузки организована алгоритмами браузера. При скачивании файлов большого объёма браузер выполнит скачивание напрямую на жесткий диск, блоками в несколько этапов, незаметных пользователю, что делает такой подход независимым от оперативной памяти компьютера. С другой стороны, разработчик клиентской части сайта не имеет возможности контролировать, регулировать и анимировать процесс загрузки файла, что иногда бывает необходимым.
Скачивание путём исполнения асинхронного запроса.
Другим подходом при скачивании файла является написание асинхронного алгоритма получения данных. Программную реализацию данного алгоритма можно обеспечить при помощи распространённых библиотек, таких как jQuery, axios, или же с использованием штатной функции языка JavaScript – fetch. Ниже представлен небольшой пример использования функции fetch:
fetch("file.pdf").then(response => {})
Более старым аналогом данной функции в рамках языка является алгоритм с использованием объекта XMLHttpRequest:
const xhr = new XMLHttpRequest();xhr.open("GET", "file.pdf", true);
xhr.responseType = "blob";
Особенностью таких алгоритмов является то, что процесс скачивания и получения полезных данных описывается программистом, разрабатывающим клиентскую часть веб-приложения. При необходимости данный процесс может вообще не отображаться пользователю. В силу того, что появляется возможность контролировать и отображать пользователю процесс загрузки в свободной анимированной форме, такой подход чаще используется программистами.
Согласно подавляющему большинству информационных источников в сети интернет, самым простым алгоритмом получения полезных данных является следующий набор строчек:
fetch("file.pdf")
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.blob();
})
.then(blob => {
// выполнение операций с объектом BLOB })
.catch(error => {
console.error("Download failed:", error);
});
В данном фрагменте инициатором процесса получения данных файла является строка: return response.blob(); 1 Однако, в условиях малой оперативной памяти компьютера, на котором размещён браузер, и большого объёма скачиваемого файла, такой алгоритм остановится, получив первый блок скачиваемых данных. Максимальный допустимый объём одного фрагмента всего файла определяется оперативной памятью, выделяемой браузеру операционной системой, и может изменятся динамически, что ведёт к ошибке неполноты скачиваемых данных и делает алгоритм неработоспособным. Если же объём файла достаточно близок к максимальному допустимому объёму одного фрагмента скачивания, то со стороны пользователя ошибка становится «плавающей» и делает работу сайта нестабильной, а диагностику неисправности более трудоёмкой.
Решением данной проблемы является использование функции response.bytes()2, которая выполнит буферизированное скачивание файла поэтапно, или же блоками:
fetch("file.pdf").then(response => {
return response.bytes();
})
.then(bytes => {
var blob = new Blob([bytes],{ type: ”application/pdf” }) // выполнение операций с объектом BLOB})
В данном примере в переменную blob будет записан объект класса Blob, ровно как и в примере с использованием функции response.blob(), рассмотренному выше, что делает эти два подхода взаимозаменяемыми.
1 Response: blob() method URL: https://developer.mozilla.org/en-US/docs/Web/API/Response/blob (дата обращения: 10.07.2025).
2 Response: blob() method URL: https://developer.mozilla.org/en-US/docs/Web/API/Response/blob (viewed: 10.07.2025).