В Javascript вы когда-нибудь задумывались, как загрузить файл, отправленный из API? Чтобы было интереснее, когда-нибудь задумывались, как скачать несколько файлов?
Давайте узнаем, как!
Предварительные требования
- В этом посте мы рассмотрим следующий API: «https://getmyfile.com/‹идентификатор файла›»
- Приведенный выше 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-архива, мы будем использовать две интересные библиотеки.
- JSZip
- 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