Как стать автором
Обновить

Как и зачем загружать файлы через ajax?

Загрузка файла на фронте – простая задача. Обычно она заключается в создании тега a и добавлением в него атрибута href. Иногда имеет смысл добавить еще и атрибут download, чтобы файл при этом не открывался в браузере. Но бывают такие случаи, когда загрузка файла таким образом невозможна.

Самый просто пример – для загрузки файла нужно указать дополнительные http заголовки. Или когда нужно следить за процессом загрузки файла и выводить это внутри gui. А может быть вы хотите вывести сообщение об успешной загрузке или сделать обработку ошибок? В таком случае может помочь загрузка файла через ajax

Для начала напишем функцию, которая собирает эмулирует загрузку файла из blob.

function downloadFileFromBlob(blob, fileName) {
  // Создаём ссылку на blob
  const urlFile = URL.createObjectURL(blob);
  // Создаём фейковый элемент <a>
  const element = document.createElement('a');
  // Формируем минимальные необходимые атрибуты
  element.setAttribute('href', urlFile);
  element.setAttribute('download', fileName);
  // Эмулируем нажатие на элемент
  element.dispatchEvent(new MouseEvent('click'));
}

При запуске этой функции происходит эмуляция загрузки файла. Но так как по сути файл загружается с диска, загрузка происходит почти мгновенно. Примерно такой же эффект можно наблюдать при загрузке файлов с mega.nz

Напишем функцию, которая получает это самый blob из сети. Разберем на примере файла первого выпавшего мне в гугле файла по запросу "example pdf file for download".

const examplePdfLink = 'http://www.africau.edu/images/default/sample.pdf';
fetch(examplePdfLink)
  .then(res => {
    // На этом этапе можно посмотреть на статус http
    // и вывести какие-нибудь сообщения в gui
    return res.blob();
  })
  .then(blobRes => {
    // Вызываем ранее написанную функцию
    downloadFileFromBlob(blobRes, 'test.pdf');
  })
  .catch(console.error)

Вместо fetch можно использовать axios

const examplePdfLink = 'http://www.africau.edu/images/default/sample.pdf';
const response = await axios.get(examplePdfLink, {
  // Явно укажем формат, в котором мы ждём ответ
  responseType: 'blob'
});
if (response.status === 200) {
  downloadFileFromBlob(response.data, 'example.pdf');
}

Так же axios предоставляет удобный интерфейс для отслеживания прогресса загрузки. Подробнее про это можно прочитать в документации. Выведем в качестве примера процент загрузки в консоль.

// Возьмём в качестве примера файл размером в 100Мб
const examplePdfLink = 'https://speed.hetzner.de/100MB.bin';
const response = await axios.get(examplePdfLink, {
  responseType: 'blob',
  onDownloadProgress: (progressEvent) => {
    console.log(
      `${(progressEvent.loaded / progressEvent.total * 100).toFixed(2)}%`
    );
  }
})
if (response.status === 200) {
  downloadFileFromBlob(response.data, 'example.pdf');
}

Для реализации этого можно использовать и библиотеки. Например, js-file-download или FileSaver.js. Моя же цель была показать, как это примерно работает под капотом.

Теги:
Хабы:
Данная статья не подлежит комментированию, поскольку её автор ещё не является полноправным участником сообщества. Вы сможете связаться с автором только после того, как он получит приглашение от кого-либо из участников сообщества. До этого момента его username будет скрыт псевдонимом.