В Javascript вы когда-нибудь задумывались, как загрузить файл, отправленный из API? Чтобы было интереснее, когда-нибудь задумывались, как скачать несколько файлов?

Давайте узнаем, как!

Предварительные требования

  1. В этом посте мы рассмотрим следующий API: «https://getmyfile.com/‹идентификатор файла›»
  2. Приведенный выше API вернет поток октетов, который должен быть загружен на стороне клиента.

Скачать один файл в JavaScript

Вызов API

Давайте воспользуемся fetch, чтобы сделать необходимый вызов API. Наш код будет выглядеть следующим образом.

export const downloadSignedFile = (fileId) => {
return fetch("https://getmyfile.com/"+fileId, requestOptions)
}

Здесь мы передаем идентификатор файла для загрузки. И вышеуказанный API вернет файл, который необходимо загрузить в клиентскую систему.

Ответ

Мы вызываем приведенный выше вызов API, передавая требуемый идентификатор файла. Опять же, этот код написан в соответствии с моими потребностями и дизайном API. Вы, безусловно, можете изменить способ вызова API в соответствии с вашим приложением.

Кроме того, ответ нашего API представляет собой большой двоичный объект. Следовательно, я использую .blob() для извлечения содержимого из ответа. Если ваш ответ представляет собой текст, вы также можете использовать .text() для извлечения содержимого.

После того, как из ответа будет извлечен BLOB-объект/текст, вам потребуется функция загрузки для завершения операции.

downloadSignedFile(fileId).then(response => response.blob())
.then(blob => {
downloadFile(blob, name)
})
.catch(error => console.log('error', error))

Загрузка

Наша функция загрузки может показаться устаревшей. В основном потому, что мы не выбираем сторонние библиотеки. Мы пытаемся добиться загрузки без помощи какой-либо сторонней библиотеки. Это означает, что вы можете использовать этот фрагмент кода, не беспокоясь о версии EcmaScript.

export const downloadFile = (blob, name="download") => {
const url = window.URL.createObjectURL(blob)
const a = document.createElement('a')
a.style.display = 'none'
a.href = url
a.download = name+".pdf"
document.body.appendChild(a)
a.click()
window.URL.revokeObjectURL(url)
setTimeout(() => {window.close()}, 5000)
}

Приведенный выше код принимает большой двоичный объект и загружает его в формате PDF на сторону клиента.

Разве это не кажется легким? Мы можем запустить указанную выше функцию в цикле и загрузить столько файлов, сколько потребуется, один за другим. Вот схема того, как можно сделать цикл.

for(let file of selectedAttachments){
const fileId = file[id];
const name = file[name];
downloadSignedFile(fileId).then(response => response.blob())
.then(blob => {
downloadFile(blob, name)
})
.catch(error => console.log('error', error))
}

Приведенный выше код перебирает массив selectedAttachments. Он получает идентификатор файла, выполняет вызов API и в конечном итоге загружает файл.

Скачать несколько файлов в JavaScript

Теперь мы хотим загрузить несколько файлов и сохранить их как файл .zip на стороне клиента. С положительной стороны, это может звучать как вариант использования, которого вы пытаетесь достичь в данный момент. Потому что было бы незрелым загружать › 2 файла один за другим в систему клиента (если это не ваше заявление о требованиях).

Чтобы загрузить несколько файлов и сохранить их в виде zip-архива, мы будем использовать две интересные библиотеки.

  1. JSZip
  2. FileSaver

Установка

Прежде всего, вам нужно установить JSZip и FileSaver.

npm install jszip
npm i [email protected]

Вызов API

Чтобы поддерживать загрузку нескольких файлов, нам пришлось бы изменить вызов API. Почему? Нам нужно дождаться завершения всех вызовов API, прежде чем архивировать и загружать их. Это будет самая сложная часть всей операции.

export const downloadSignedFiles = async (fileId) => {
return new Promise((resolve) => {
fetch("https://getmyfile.com/"+fileId, requestOptions)
.then((response) => response.blob())
.then((blob) => {
resolve(blob)
})
})
}

Здесь мы используем интересную концепцию «обещания» в JavaScript.

По определению Promises можно использовать для ожидания завершения всех асинхронных вызовов перед выполнением следующей строки кода. У него есть два свойства: состояние и результат. Обещания по-прежнему полны энтузиазма, чтобы операция была успешно завершена. У него всегда будет причина, по которой операция не была завершена. Или почему операция завершилась успешно.

Вызов измененного приложения возвращает Promise. Как только ответ BLOB-объекта получен, он разрешается. Затем обещание возвращается вызывающей стороне.

Ответ

В логике загрузки нескольких файлов ответ помещается в массив. В этом массиве будут храниться все промисы, в которых будет загружаться большой двоичный объект.

После того, как все обещания получены и сохранены в downloadRequests, нам нужно выполнить еще несколько шагов, прежде чем завершить загрузку.

const getAllBlobs = async (downloadArray = selectedAttachments) => {
let downloadRequests = [];
for(let file of downloadArray){
const fileId = file[id];
let name = file[name];
downloadRequests.push({name:name, blob:downloadSignedFiles(fileId)})
}
return downloadRequests
}

Загрузка

Теперь нам нужно завершить загрузку. Для этого мы должны работать со всеми промисами, которые хранятся в массиве downloadRequest.

const getMegatronZip = async (downloadArray) => {
var zip = new JSZip();
var folder = zip.folder("download");
let downloadRequests = getAllBlobs(downloadArray);
Promise.all(downloadRequests).then((response) => {
response.forEach((item) => {
folder.file(item.name, item.blob);
})
})
return zip;
}

Роль Promise.all заключается в том, чтобы принимать массив обещаний, обрабатывать их и возвращать одно обещание. Окончательный промис будет массивом, содержащим результат каждого обработанного промиса. Если какое-либо из обещаний было отклонено, Promise.all отклонит всю операцию.

В нашем случае каждое обещание в downloadRequests будет иметь загруженный большой двоичный объект. Итак, если вы выберете три файла, будет три разрешенных промиса с тремя блобами. Что нам нужно сделать сейчас, так это сохранить большой двоичный объект в файле внутри zip-папки. Это первый шаг, на котором мы создаем zip-архив со всеми загружаемыми файлами. Для этого мы используем JSZip.

Как разработчик, я твердо верю в необходимость не изобретать велосипед. Конечно, вы можете написать собственную библиотеку для создания zip-архива, но зачем вам это? Когда у вас уже есть JSZip, чтобы сделать все необходимое.

import { saveAs } from 'file-saver';
const downloadAll = async () => {
let megatronBlob = await getMegatronZip();
megatronBlob.generateAsync({type:"blob"})
.then(function(content) {
saveAs(content, "download.zip");
});
}

Наконец, мы используем файл-сохранитель, чтобы сохранить zip-файл в системе. Помните, что вы можете использовать ту же библиотеку для сохранения одного файла и на стороне клиента. Тем не менее, мы пытаемся рассмотреть все возможные способы достижения загрузки в JavaScript. Хотели бы, чтобы вы попробовали, как загрузить один файл с помощью файловой заставки, и поделитесь своим комментарием в этом посте.

generateAsync — это функция в JSZip, которая создает необходимый ZIP-файл.

saveAs — это функция, которая использует содержимое zip-файла из JSZip и сохраняет zip-архив на стороне клиента.

Удачной загрузки!

Теперь мы узнали два способа загрузки файлов на стороне клиента. Вы можете загрузить один файл или заархивировать несколько файлов и сохранить их. Вышеуказанные функции могут быть реализованы в любом проекте React, простом ванильном JS или Angular без каких-либо проблем или хлопот.

Если вам понравился этот пост, подпишитесь и поделитесь им, чтобы продолжить обучение!

Дополнительные материалы на plainenglish.io