⚠️Эта статья является первой подписанной статьей сообщества Nuggets, и её перепечатка без разрешения запрещена.
существуетЗагрузка файла, достаточно понять эти 8 сценариев (1452 👍)После того, как эта статья была опубликована, брат Абао получил множество сообщений от друзей, в которых они благодарили их за ободрение и поддержку. Среди них копать друзей@Мой туманный дождь не в Цзяннанеа также@rainxВнизу статьи были размещены следующие комментарии:
Так как у копателя есть просьба, и даже название было придумано для Брата Абао, давайте подведем итоги всей статьи по сцене скачивания файла.
Вообще, в нашей работе в основном используется 9 сценариев загрузки файлов, каждый из которых использует разные технологии, и есть много деталей, требующих нашего дополнительного внимания. Сегодня Brother A Bao расскажет вам об этих 9 сценариях, чтобы вы могли легко справиться с различными сценариями загрузки. Прочитав эту статью, вы будете знать следующее:
При обработке файлов на стороне браузера мы часто используемBlob. Например, локальный предварительный просмотр изображений, сжатие изображений, загрузка больших файлов по частям и загрузка файлов. В сценарии загрузки файла на стороне браузера, о котором мы сегодня поговоримзагрузка тега,showSaveFilePicker API Загрузка,ZIP-загрузкаВ других сценариях он будет использоватьсяBlob, поэтому нам необходимо освоить связанные с ним знания, прежде чем изучать конкретное приложение, что может помочь нам лучше понять пример кода.
1. Базовые знания
1.1 Общие сведения о больших двоичных объектах
Blob(Большой двоичный объект) представляет собой большой объект двоичного типа. В системе управления базами данных двоичные данные хранятся как набор одного объекта. Блобы обычно представляют собой изображения, звуковые или мультимедийные файлы.Объект типа Blob в JavaScript представляет собой неизменяемый примитивный объект, похожий на файл данных.Его данные могут быть прочитаны в текстовом или двоичном формате или преобразованы вReadableStreamдля манипулирования данными.
Blob
объект состоит из необязательной строкиtype
(как правилоMIMEтипа) иblobParts
сочинение:
В JavaScript вы можете создавать объекты Blob с помощью конструктора Blob,Конструктор больших двоичных объектовСинтаксис следующий:
const aBlob = new Blob(blobParts, options);
Соответствующие параметры описываются следующим образом:
- blobParts: это массив объектов, таких как ArrayBuffer, ArrayBufferView, Blob, DOMString и т. д. DOMStrings будут закодированы как UTF-8.
- options: необязательный объект, содержащий следующие два свойства:
- тип - значение по умолчанию
""
, который представляет тип MIME содержимого массива, которое будет помещено в большой двоичный объект. - окончания - значение по умолчанию
"transparent"
, используется для указания окончания строки, включающей\n
как пишется строка. Это одно из следующих двух значений:"native"
, что означает, что символ конца строки заменяется символом новой строки, подходящим для файловой системы основной операционной системы, или"transparent"
, что означает, что терминатор, сохраненный в большом двоичном объекте, останется неизменным.
- тип - значение по умолчанию
1.2 Общие сведения об URL-адресах BLOB-объектов
URL-адрес BLOB-объекта/URL-адрес объекта — это псевдопротокол, который позволяет использовать объекты Blob и File в качестве источников URL-адресов для изображений, ссылок для загрузки двоичных данных и многого другого. В браузере мы используемURL.createObjectURL
метод создания URL-адреса BLOB-объекта, который принимаетBlob
объекта и создать для него уникальный URL видаblob:<origin>/<uuid>
, соответствующий пример выглядит следующим образом:
blob:http://localhost:3000/53acc2b6-f47b-450f-a390-bf0665e04e59
Внутри браузера для каждого проходаURL.createObjectURL
Сгенерированный URL хранитURL-адрес → большой двоичный объекткарта. Поэтому такие URL короче, но к ним можно получить доступBlob
. Сгенерированный URL-адрес действителен только в том случае, если текущий документ открыт. это позволяет цитировать<img>
,<a>
серединаBlob
, но если вы перейдете по URL-адресу большого двоичного объекта, которого больше не существует, вы получите ошибку 404 в своем браузере.
Приведенный выше URL-адрес большого двоичного объекта выглядит великолепно, но на самом деле он имеет побочные эффекты.Хотя сопоставление URL-адрес → BLOB-объект сохраняется, сам BLOB-объект все еще находится в памяти, и браузер не может его освободить. Сопоставление автоматически очищается, когда документ выгружается, поэтому объект Blob затем освобождается.. Однако если приложение работает долго, большой двоичный объект не будет освобожден браузером в течение короткого периода времени. Поэтому, если вы создадите URL-адрес большого двоичного объекта, он будет находиться в памяти, даже если большой двоичный объект больше не нужен.
Для этой проблемы вы можете позвонитьURL.revokeObjectURL(url)
удаляет ссылку из внутренней карты, позволяя удалить большой двоичный объект, если нет других ссылок, и освобождает память.
Теперь, когда вы знаете о больших двоичных объектах и URL-адресах больших двоичных объектов, если вы все еще хотите узнать больше о больших двоичных объектах, прочтитеКапли, о которых вы не зналиЭта статья. Приступим к представлению сцены загрузки файла клиента.
С непрерывным развитием веб-технологий функции браузеров также становятся все более и более мощными. За прошедшие годы появилось множество онлайн-инструментов веб-дизайна, таких как онлайн-PS, онлайн-дизайнер плакатов или онлайн-дизайнер пользовательских форм и т. Д. Эти веб-дизайнеры позволяют пользователям сохранять сгенерированные файлы локально после завершения проектирования, а некоторые из них используют веб-API, предоставляемый браузером, для загрузки файлов на стороне клиента. Ниже брат Бао сначала представит наиболее распространенные клиентские загрузки.загрузка тегастроить планы.
2. загрузка тега
html
<h3>a 标签下载示例</h3>
<div>
<img src="../images/body.png" />
<img src="../images/eyes.png" />
<img src="../images/mouth.png" />
</div>
<img id="mergedPic" src="http://via.placeholder.com/256" />
<button onclick="merge()">图片合成</button>
<button onclick="download()">图片下载</button>
В приведенном выше коде мы передаемimg
Этикетка ссылается на следующие 3 материала:
когда пользователь нажимаетсинтез изображенийкнопку, составное изображение будет отображаться вimg#mergedPic
в контейнере. После успешного синтеза изображения пользователь может щелкнутьзагрузка изображениякнопку для загрузки синтезированного изображения на локальный. Соответствующий рабочий процесс показан на следующем рисунке:
Как видно из рисунка выше, общий процесс работы относительно прост. Далее давайте посмотрим насинтез изображенийа такжезагрузка изображениялогика реализации.
js
Функцию синтеза изображений Brother Abao использует напрямую на Github.merge-imagesЭта сторонняя библиотека для достижения. Воспользуйтесь преимуществами библиотекиmergeImages(images, [options])
метод, мы можем легко реализовать функцию синтеза изображения. После вызова этого метода он вернетPromiseобъект, когда асинхронная операция будет завершена, составное изображение будетData URLsформат возвращен.
const mergePicEle = document.querySelector("#mergedPic");
const images = ["/body.png", "/eyes.png", "/mouth.png"].map(
(path) => "../images" + path
);
let imgDataUrl = null;
async function merge() {
imgDataUrl = await mergeImages(images);
mergePicEle.src = imgDataUrl;
}
Функция загрузки изображений естьdataUrlToBlob
а такжеsaveFile
Эти две функции реализованы. Они используются для достиженияData URLs => BlobПреобразование и сохранение файла, конкретный код выглядит следующим образом:
function dataUrlToBlob(base64, mimeType) {
let bytes = window.atob(base64.split(",")[1]);
let ab = new ArrayBuffer(bytes.length);
let ia = new Uint8Array(ab);
for (let i = 0; i < bytes.length; i++) {
ia[i] = bytes.charCodeAt(i);
}
return new Blob([ab], { type: mimeType });
}
// 保存文件
function saveFile(blob, filename) {
const a = document.createElement("a");
a.download = filename;
a.href = URL.createObjectURL(blob);
a.click();
URL.revokeObjectURL(a.href)
}
Поскольку предметом этой статьи является введение загрузки файлов, давайте сосредоточимся на анализеsaveFile
функция. Внутри этой функции мы использовалиHTMLAnchorElement.downloadАтрибут, значение которого представляет имя загруженного файла. Если имя не является допустимым именем файла для операционной системы, браузер изменит его. Кроме того, этот атрибут указывает, что связанный ресурс будет загружен, а не отображен в браузере.
должны знать о том,download
Существует проблема совместимости с атрибутом.Например, IE 11 и более ранние версии не поддерживают этот атрибут, как показано на следующем рисунке:
(Источник изображения:руб. newser.com/download)
При установке элементаdownload
собственность, мы позвонимURL.createObjectURL
метод для создания URL-адреса объекта и назначения возвращенного URL-адреса элементуhref
Атрибуты. Затем, вызвав элементclick
метод для запуска операции загрузки файла, которая будет вызываться один раз в концеURL.revokeObjectURL
удаляет ссылку из внутренней карты, позволяя удалить большой двоичный объект, если нет других ссылок, и освобождает память.
озагрузка тегаЭто содержание введения, давайте представим, как использовать новый веб-API -showSaveFilePicker
Реализовать загрузку файлов.
пример загрузки тега:a-tag
Три, загрузка API showSaveFilePicker
showSaveFilePickerAPI естьWindow
Метод, определенный в интерфейсе. После вызова этого метода будет отображаться средство выбора файла, позволяющее пользователю выбрать путь для сохранения. Сигнатура метода выглядит так:
let FileSystemFileHandle = Window.showSaveFilePicker(options);
showSaveFilePickerМетод поддерживает необязательный параметр типа object, который может содержать следующие свойства:
-
excludeAcceptAllOption
: Логический тип, значение по умолчаниюfalse
. По умолчанию селектор должен содержать параметр, позволяющий не применять фильтры по типам файлов (как указано в следующемtypes
опция включена). Установите этот параметр наtrue
иметь в видуtypes
Варианты недоступны. -
types
: Тип массива, указывающий список типов файлов, которые разрешено сохранять. Каждый элемент массива представляет собой объект конфигурации, содержащий следующие свойства:-
description(可选)
: Используется для описания категории типов файлов, которые разрешено сохранять. -
accept
: это объект, чейkey
даMIMEТип, значение — список расширений файлов.
-
передачаshowSaveFilePickerметод, он вернетFileSystemFileHandleобъект. С помощью этого объекта вы можете вызывать методы объекта для управления файлом. Например, вызовcreateWritableметод, он вернетFileSystemWritableFileStreamобъект, вы можете записать данные в файл. Конкретное использование заключается в следующем:
async function saveFile(blob, filename) {
try {
const handle = await window.showSaveFilePicker({
suggestedName: filename,
types: [
{
description: "PNG file",
accept: {
"image/png": [".png"],
},
},
{
description: "Jpeg file",
accept: {
"image/jpeg": [".jpeg"],
},
},
],
});
const writable = await handle.createWritable();
await writable.write(blob);
await writable.close();
return handle;
} catch (err) {
console.error(err.name, err.message);
}
}
function download() {
if (!imgDataUrl) {
alert("请先合成图片");
return;
}
const imgBlob = dataUrlToBlob(imgDataUrl, "image/png");
saveFile(imgBlob, "face.png");
}
При использовании вышеуказанного обновленногоsaveFile
Функция, при сохранении составного изображения отображается следующий селектор файла сохранения:
Как видно из приведенного выше рисунка, по сравнению сзагрузка тегаПуть,showSaveFilePickerAPI позволяет вам выбрать каталог загрузки файла, выбрать формат сохранения файла и изменить имя сохраненного файла. Смотри сюда, тебе не кажетсяshowSaveFilePickerФункция API довольно мощная, но, к сожалению, совместимость API в настоящее время не очень хорошая, как показано на следующем рисунке:
(Источник изображения:потрите newser.com/?search=yes о…
фактическиshowSaveFilePickerдаFile System Accessметоды, определенные в API, за исключениемshowSaveFilePickerКроме того, естьshowOpenFilePickerа такжеshowDirectoryPickerи другие методы. Если вы хотите использовать эти API в реальном проекте, вы можете рассмотреть возможность их использования.GoogleChromeLabsОткрытый исходный кодbrowser-fs-accessЭта библиотека упрощает использование на поддерживаемых платформах.File System AccessAPI для неподдерживаемых платформ будет автоматически понижен до использования<input type="file">
а также<a download>
Путь.
Может быть, всеbrowser-fs-accessЭта библиотека будет относительно незнакомой, но если вы замените ее наFileSaver.jsЭта библиотека должна быть вам знакома. Далее мы расскажем, как использоватьFileSaver.jsЭта библиотека реализует загрузку файлов на стороне клиента.
Пример загрузки API showSaveFilePicker:save-file-picker
4. Скачать FileSaver
FileSaver.jsРешение для сохранения файлов на стороне клиента, идеально подходящее для веб-приложений, генерирующих файлы на стороне клиента. Это HTML5-версия реализации saveAs() FileSaver, которая поддерживает большинство основных браузеров, и ее совместимость показана на следующем рисунке:
(Источник изображения:GitHub.com/Eli GRE собирается/FI…
во введенииFileSaver.jsПосле этой библиотеки мы можем использоватьsaveAs
метод сохранения файла. Сигнатура, соответствующая этому методу, выглядит следующим образом:
FileSaver saveAs( Blob/File/Url, optional DOMString filename, optional Object { autoBom } )
Метод saveAs поддерживает 3 параметра, первый параметр указывает, что он поддерживаетBlob/File/Url
Три типа, 2-й параметр представляет имя файла (необязательно), а 3-й параметр представляет объект конфигурации (необязательно). если тебе нужноFlieSaver.jsПодсказки кодировки текста Unicode предоставляются автоматически (ссылка:Знак порядка байтов), необходимо установить{ autoBom: true}
.
Разобравшись с методом saveAs, давайте приведем 3 конкретных примера использования:
1. Сохраните текст
let blob = new Blob(["大家好,我是阿宝哥!"], { type: "text/plain;charset=utf-8" });
saveAs(blob, "hello.txt");
2. Сохраняйте онлайн-ресурсы
saveAs("https://httpbin.org/image", "image.jpg");
Если загруженный URL относится к тому же домену, что и текущий сайт, он будет использоватьсяa[download]
способ скачать. В противном случае он будет использоваться первымСинхронизированный запрос HEADопределить, поддерживать лиCORSМеханизм, если он поддерживается, будет выполнять загрузку данных и использовать URL-адреса BLOB-объектов для загрузки файлов. если не поддерживаетсяCORSмеханизм, попробую использоватьa[download]
способ скачать.
Стандартный файловый API W3CBlobИнтерфейс доступен не во всех браузерах, для решения этой проблемы вы можете использоватьBlob.jsдля решения проблем совместимости.
(Источник изображения:потрите news.com/?search=no…
3. Сохраните содержимое холста.
let canvas = document.getElementById("my-canvas");
canvas.toBlob(function(blob) {
saveAs(blob, "abao.png");
});
должен быть в курсеcanvas.toBlob()
метод доступен не во всех браузерах, для этой проблемы вы можете использоватьcanvas-toBlob.jsдля решения проблем совместимости.
(Источник изображения:потрите newser.com/?search=to B…
После введения примера использования метода saveAs давайте обновим предыдущий пример.download
метод:
function download() {
if (!imgDataUrl) {
alert("请先合成图片");
return;
}
const imgBlob = dataUrlToBlob(imgDataUrl, "image/png");
saveAs(imgBlob, "face.png");
}
Очевидно, что после использования метода saveAs загрузить синтезированное изображение очень просто. если ты правFileSaver.jsЕсли вам интересно, как это работает, вы можете прочитатьДавайте поговорим о 15.5K FileSaver, как он работает?Эта статья. Сценарии, представленные выше, предназначены для прямой загрузки одного файла.На самом деле, мы также можем одновременно загружать несколько файлов на клиенте, а затем сжимать загруженные файлы в Zip-пакет и загружать его локально.
Пример загрузки FileSaver:file-saver
5. ZIP-загрузка
существуетЗагрузка файлов, достаточно понять эти 8 сценариевВ этой статье брат Абао рассказывает, как использоватьJSZipпредоставляется этой библиотекойAPI, сожмите все файлы в каталоге для загрузки в ZIP-файл, а затем загрузите сгенерированный ZIP-файл на сервер. Аналогичным образом, используяJSZipС помощью этой библиотеки мы можем одновременно загружать несколько файлов на стороне клиента, а затем сжимать загруженные файлы в Zip-пакет и загружать их локально. Соответствующий рабочий процесс показан на следующем рисунке:
На приведенном выше Gif-изображении брат А. Бао демонстрирует процесс упаковки 3 материальных изображений в Zip-файлы и загрузки их локально. Далее давайте представим, как использоватьJSZipЭта библиотека реализует вышеуказанные функции.
html
<h3>Zip 下载示例</h3>
<div>
<img src="../images/body.png" />
<img src="../images/eyes.png" />
<img src="../images/mouth.png" />
</div>
<button onclick="download()">打包下载</button>
js
const images = ["body.png", "eyes.png", "mouth.png"];
const imageUrls = images.map((name) => "../images/" + name);
async function download() {
let zip = new JSZip();
Promise.all(imageUrls.map(getFileContent)).then((contents) => {
contents.forEach((content, i) => {
zip.file(images[i], content);
});
zip.generateAsync({ type: "blob" }).then(function (blob) {
saveAs(blob, "material.zip");
});
});
}
// 从指定的url上下载文件内容
function getFileContent(fileUrl) {
return new JSZip.external.Promise(function (resolve, reject) {
// 调用jszip-utils库提供的getBinaryContent方法获取文件内容
JSZipUtils.getBinaryContent(fileUrl, function (err, data) {
if (err) {
reject(err);
} else {
resolve(data);
}
});
});
}
В приведенном выше коде, когда пользователь нажимаетСкачать пакеткнопка, она будет называтьсяdownload
функция. Внутри этой функции он сначала вызоветJSZip
создание конструктораJSZip
объект, затем используйтеPromise.allфункция, обеспечивающая загрузку всех файлов перед вызовомfile(name, data [,options])
способ, добавьте загруженный файл в ранее созданныйJSZip
в объекте. наконец прошлоzip.generateAsync
функция для создания Zip-файла и использованияFileSaver.jsкоторый предоставилsaveAs
метод сохранения Zip-файла.
Пример загрузки в формате ZIP:Zip
6. Скачать в виде вложений
В сценарии загрузки на стороне сервера загрузка в виде вложения является относительно распространенным сценарием. В этом сценарии мы устанавливаемContent-Disposition
Заголовок ответа указывает, в какой форме отображается содержимое ответа, является ли оно встроенным или загруженным и сохраненным локально в виде вложения.
Content-Disposition: inline
Content-Disposition: attachment
Content-Disposition: attachment; filename="mouth.png"
В случае форм HTTP,Content-Disposition
также может использоваться какmultipart bodyзаголовки сообщений в:
Content-Disposition: form-data
Content-Disposition: form-data; name="fieldName"
Content-Disposition: form-data; name="fieldName"; filename="filename.jpg"
1-й параметр всегда фиксированform-data
; Дополнительные параметры нечувствительны к регистру и имеют значения параметров, используя знак равенства (=
), а значение параметра заключено в двойные кавычки. Используйте точку с запятой между параметрами (;
) разделены.
пониматьContent-Disposition
После функции посмотрим, как реализовать функцию загрузки в виде вложений.KoaЭто простой и удобный веб-фреймворк, отличающийся элегантностью, простотой, малым весом и высокой степенью свободы. Поэтому мы выбираем его для создания файлового сервиса и использования@koa/routerПромежуточное ПО для обработки маршрутизации:
// attachment/file-server.js
const fs = require("fs");
const path = require("path");
const Koa = require("koa");
const Router = require("@koa/router");
const app = new Koa();
const router = new Router();
const PORT = 3000;
const STATIC_PATH = path.join(__dirname, "./static/");
// http://localhost:3000/file?filename=mouth.png
router.get("/file", async (ctx, next) => {
const { filename } = ctx.query;
const filePath = STATIC_PATH + filename;
const fStats = fs.statSync(filePath);
ctx.set({
"Content-Type": "application/octet-stream",
"Content-Disposition": `attachment; filename=${filename}`,
"Content-Length": fStats.size,
});
ctx.body = fs.createReadStream(filePath);
});
// 注册中间件
app.use(async (ctx, next) => {
try {
await next();
} catch (error) {
// ENOENT(无此文件或目录):通常是由文件操作引起的,这表明在给定的路径上无法找到任何文件或目录
ctx.status = error.code === "ENOENT" ? 404 : 500;
ctx.body = error.code === "ENOENT" ? "文件不存在" : "服务器开小差";
}
});
app.use(router.routes()).use(router.allowedMethods());
app.listen(PORT, () => {
console.log(`应用已经启动:http://localhost:${PORT}/`);
});
Приведенный выше код сохраняется вattachment
в каталогеfile-server.js
файл, так же естьstatic
Подкаталоги используются для хранения статических ресурсов. В настоящее времяstatic
Каталог содержит следующие 3 файла png.
├── file-server.js
└── static
├── body.png
├── eyes.png
└── mouth.png
когда ты бежишьnode file-server.js
После того, как команда успешно запустит файловый сервер, его можно загрузить через правильный URL-адрес.static
файлы в каталоге. например открыть в браузереhttp://localhost:3000/file?filename=mouth.png
Этот адрес, вы начнете загрузкуmouth.png
документ. Если указанный файл не существует, он вернет файл не существует.
KoaЯдро очень простое, а расширенные функции реализованы через промежуточное ПО. такие как общие маршруты,CORS, статическая обработка ресурсов и другие функции реализуются через промежуточное ПО. Итак, чтобы освоитьKoaЯдром этой структуры является освоение механизма промежуточного программного обеспечения. Если вы хотите узнать большеKoa, ты можешь читатьКак лучше понять промежуточное ПО и луковую модельЭта статья.
При написании веб-страниц в формате HTML для некоторых простых изображений обычно выбирают прямое встраивание содержимого изображения в веб-страницу, тем самым уменьшая ненужные сетевые запросы, но данные изображения являются двоичными данными, как их встроить? Большинство современных браузеров поддерживаютData URLsособенности, которые позволяют использоватьBase64Кодирует двоичные данные изображения или другого файла и встраивает их в веб-страницу в виде текстовой строки. Таким образом, файл также может быть передан черезBase64Формат для передачи, далее мы расскажем, как загружать изображения в формате Base64.
Скачать пример во вложении:attachment
Семь, загрузка в формате base64
Base64представляет собой представление двоичных данных, основанное на 64 печатных символах. Поскольку 2⁶ = 64, каждые 6 бит являются единицей, соответствующей печатному символу. 3 байта имеют 24 бита, что соответствует 4 единицам base64, т. е. 3 байта могут быть представлены 4 печатными символами. Соответствующий процесс преобразования показан на следующем рисунке:
Base64 обычно используется при обработке текстовых данных для представления, передачи и хранения некоторых двоичных данных, включая электронные письма MIME и некоторые сложные данные в XML.существуетMIMEФормат электронной почты, base64 может использоваться для кодирования данных двоичной последовательности байтов в текст, состоящий из последовательности символов ASCII. При использовании укажите base64 в методе кодирования передачи. Используемые символы включают 26 прописных и строчных латинских букв, 10 цифр, знак плюс + и косую черту /, всего 64 символа, а знак равенства = используется в качестве суффикса.
Сначала здесь представлено связанное с Base64 содержимое.Если вы хотите узнать больше о Base64, вы можете прочитатьОдна статья для понимания кодировки base64Эта статья. Давайте посмотрим на конкретный код реализации:
7.1 Интерфейсный код
html
В следующем HTML-коде мы передаемselect
элемент, позволяющий пользователю выбрать изображение для загрузки. Когда пользователь переключается между разными изображениями,img#imgPreview
Изображение, отображаемое в элементе, изменяется соответствующим образом.
<h3>base64 下载示例</h3>
<img id="imgPreview" src="./static/body.png" />
<select id="picSelect">
<option value="body">body.png</option>
<option value="eyes">eyes.png</option>
<option value="mouth">mouth.png</option>
</select>
<button onclick="download()">下载</button>
js
const picSelectEle = document.querySelector("#picSelect");
const imgPreviewEle = document.querySelector("#imgPreview");
picSelectEle.addEventListener("change", (event) => {
imgPreviewEle.src = "./static/" + picSelectEle.value + ".png";
});
const request = axios.create({
baseURL: "http://localhost:3000",
timeout: 60000,
});
async function download() {
const response = await request.get("/file", {
params: {
filename: picSelectEle.value + ".png",
},
});
if (response && response.data && response.data.code === 1) {
const fileData = response.data.data;
const { name, type, content } = fileData;
const imgBlob = base64ToBlob(content, type);
saveAs(imgBlob, name);
}
}
Когда пользователь выбирает изображение для загрузки и нажимает кнопку загрузки, вызывается приведенный выше код.download
функция. Внутри этой функции мы используем преимущество экземпляра axios.get
Метод инициирует HTTP-запрос для получения указанного изображения. Поскольку возвращаемое изображение находится в формате base64, при вызове файла, предоставленного FileSaversaveAs
Перед методом нам нужно преобразовать строку Base64 в объект BLOB, что осуществляется с помощью следующегоbase64ToBlob
Функция завершена, и конкретная реализация этой функции выглядит следующим образом:
function base64ToBlob(base64, mimeType) {
let bytes = window.atob(base64);
let ab = new ArrayBuffer(bytes.length);
let ia = new Uint8Array(ab);
for (let i = 0; i < bytes.length; i++) {
ia[i] = bytes.charCodeAt(i);
}
return new Blob([ab], { type: mimeType });
}
7.2 Код сервера
// base64/file-server.js
const fs = require("fs");
const path = require("path");
const mime = require("mime");
const Koa = require("koa");
const cors = require("@koa/cors");
const Router = require("@koa/router");
const app = new Koa();
const router = new Router();
const PORT = 3000;
const STATIC_PATH = path.join(__dirname, "./static/");
router.get("/file", async (ctx, next) => {
const { filename } = ctx.query;
const filePath = STATIC_PATH + filename;
const fileBuffer = fs.readFileSync(filePath);
ctx.body = {
code: 1,
data: {
name: filename,
type: mime.getType(filename),
content: fileBuffer.toString("base64"),
},
};
});
// 注册中间件
app.use(async (ctx, next) => {
try {
await next();
} catch (error) {
ctx.body = {
code: 0,
msg: "服务器开小差",
};
}
});
app.use(cors());
app.use(router.routes()).use(router.allowedMethods());
app.listen(PORT, () => {
console.log(`应用已经启动:http://localhost:${PORT}/`);
});
В приведенном выше коде операция кодирования изображения Base64 определена в/file
в обработчике маршрута, соответствующем маршруту. Когда сервер получает запрос на загрузку файла, инициированный клиентом, напримерGET /file?filename=body.png HTTP/1.1
, это будет изctx.query
попасть на объектfilename
параметр. Этот параметр указывает имя файла.После получения имени файла мы можем соединить абсолютный путь к файлу, а затем передать файл, предоставленный платформой Node.js.fs.readFileSync
метод для чтения содержимого файла, этот метод вернетBufferобъект. После успешного чтения содержимого файла переходим к вызовуBufferобъектtoString
Метод выполняет кодирование содержимого файла в формате Base64, и окончательное загруженное изображение будет возвращено клиенту в формате Base64.
Скачать пример в формате base64:base64
Восемь, фрагментированная загрузка
Поблочное кодирование передачи в основном используется в сценариях, когда необходимо передать большой объем данных, но длина ответа не может быть получена до тех пор, пока запрос не будет обработан. Например, когда вам нужно сгенерировать большую HTML-таблицу с данными, полученными из запроса к базе данных, или когда вам нужно передать большое количество изображений.
Чтобы использовать фрагментированное кодирование передачи, вам необходимо настроить заголовок ответаTransfer-Encoding
поле и установите его значение равнымchunked
илиgzip, chunked
:
Transfer-Encoding: chunked
Transfer-Encoding: gzip, chunked
заголовок ответаTransfer-Encoding
Значение поляchunked
,表示数据以一系列分块的形式进行发送。 должен быть в курсеTransfer-Encoding
а такжеContent-Length
Эти два поля являются взаимоисключающими, что означает, что эти два поля не могут появляться в ответном пакете одновременно. Давайте посмотрим на правила кодирования для блочной передачи:
- Каждый блок состоит из двух частей: длины блока и блока данных;
- Длина блока выражается в виде шестнадцатеричного числа, где
\r\n
конец; - Блок данных следует сразу за длиной блока, также используя
\r\n
конец, но данные не содержат\r\n
; -
0\r\n\r\n
.
После понимания правил кодирования переноса блока Давайте посмотрим, как использовать кодировку передачи блоков для загрузки файлов.
8.1 Интерфейсный код
html5
<h3>chunked 下载示例</h3>
<button onclick="download()">下载</button>
js
const chunkedUrl = "http://localhost:3000/file?filename=file.txt";
function download() {
return fetch(chunkedUrl)
.then(processChunkedResponse)
.then(onChunkedResponseComplete)
.catch(onChunkedResponseError);
}
function processChunkedResponse(response) {
let text = "";
let reader = response.body.getReader();
let decoder = new TextDecoder();
return readChunk();
function readChunk() {
return reader.read().then(appendChunks);
}
function appendChunks(result) {
let chunk = decoder.decode(result.value || new Uint8Array(), {
stream: !result.done,
});
console.log("已接收到的数据:", chunk);
console.log("本次已成功接收", chunk.length, "bytes");
text += chunk;
console.log("目前为止共接收", text.length, "bytes\n");
if (result.done) {
return text;
} else {
return readChunk();
}
}
}
function onChunkedResponseComplete(result) {
let blob = new Blob([result], {
type: "text/plain;charset=utf-8",
});
saveAs(blob, "hello.txt");
}
function onChunkedResponseError(err) {
console.error(err);
}
когда пользователь нажимаетскачатькнопка, приведенный выше код будет вызыватьсяdownload
функция. Внутри этой функции мы будем использоватьFetch APIдля выполнения операции загрузки. Поскольку данные на стороне сервера отправляются сериями фрагментов, на стороне браузера мы получаем их в виде потока. то есть черезresponse.body
стать читаемымReadableStream, затем используйтеReadableStream.getReader()
Создайте читалку и, наконец, позвонитеreader.read
метод для чтения возвращенных фрагментированных данных.
потому чтоfile.txt
Содержимое файла представляет собой обычный текст, иresult.value
ЗначениеUint8Arrayтип данных, поэтому при обработке возвращенных фрагментированных данных мы используемTextDecoderтекстовый декодер. Декодер поддерживает только одну конкретную кодировку текста, например.utf-8
,iso-8859-2
,koi8
,cp1261
,gbk
и т.п.
Если полученный фрагмент неоконечный блок,result.done
Значениеfalse
, он будет продолжать звонитьreadChunk
метод чтения фрагментированных данных. и при полученииоконечный блокПосле этого он указывает, что фрагментированные данные были переданы. В настоящее время,result.done
собственность вернетсяtrue
. будет автоматически вызыватьonChunkedResponseComplete
внутри этой функции мы создаем объект Blob с декодированным текстом в качестве параметра. После этого продолжайте использовать файлы, предоставленные библиотекой FileSaver.saveAs
Метод реализует загрузку файла.
Здесь мы используемWiresharkИнструмент анализа сетевых пакетов, который захватывает пакет. В частности, как показано на рисунке ниже:
Из рисунка хорошо видно, чтоHTTP chunked responseсодержит нижеБлок данныха такжеКонец фрагментированного кодирования. Далее давайте посмотрим на серверный код.
8.2 Код сервера
const fs = require("fs");
const path = require("path");
const Koa = require("koa");
const cors = require("@koa/cors");
const Router = require("@koa/router");
const app = new Koa();
const router = new Router();
const PORT = 3000;
router.get("/file", async (ctx, next) => {
const { filename } = ctx.query;
const filePath = path.join(__dirname, filename);
ctx.set({
"Content-Type": "text/plain;charset=utf-8",
});
ctx.body = fs.createReadStream(filePath);
});
// 注册中间件
app.use(async (ctx, next) => {
try {
await next();
} catch (error) {
// ENOENT(无此文件或目录):通常是由文件操作引起的,这表明在给定的路径上无法找到任何文件或目录
ctx.status = error.code === "ENOENT" ? 404 : 500;
ctx.body = error.code === "ENOENT" ? "文件不存在" : "服务器开小差";
}
});
app.use(cors());
app.use(router.routes()).use(router.allowedMethods());
app.listen(PORT, () => {
console.log(`应用已经启动:http://localhost:${PORT}/`);
});
существует/file
В процессоре маршрута мы сначала проходимctx.query
получатьfilename
имя файла, затем объедините абсолютный путь к файлу, а затем передайтеfs.createReadStream
метод для создания читаемого потока. Наконец, назначьте созданный читаемый поток наctx.body
атрибут для возврата данных изображения клиенту.
Теперь, когда мы знаем, что можем использовать блочное кодирование передачи (Transfer-Encoding) для блочной передачи данных, есть ли способ получить данные файла в пределах указанного диапазона? Для этой проблемы мы можем использовать запрос диапазона протокола HTTP. Далее мы опишем, как использовать HTTP-запросы диапазона для загрузки указанного диапазона данных.
Пример скачанной загрузки:chunked
9. Объем загрузки
Запросы области протокола HTTP позволяют серверу отправлять клиенту только часть сообщения HTTP. Запросы диапазона полезны при передаче больших медиафайлов или в сочетании с функцией возобновления загрузки файла. если присутствует в ответеAccept-Ranges
заголовок (и его значение не "none"), то сервер поддерживает запросы диапазона.
В заголовке Range можно одновременно запросить несколько частей, и сервер вернет их в виде составного файла. Если сервер возвращает ответ диапазона, вам нужно использовать206 Partial Contentкод состояния. Если запрошенный диапазон недействителен, сервер вернет416 Range Not SatisfiableКод состояния, указывающий на ошибку клиента. Серверу разрешено игнорировать заголовок Range и, таким образом, возвращать весь файл с кодом состояния 200 .
Синтаксис диапазона:
Range: <unit>=<range-start>-
Range: <unit>=<range-start>-<range-end>
Range: <unit>=<range-start>-<range-end>, <range-start>-<range-end>
Range: <unit>=<range-start>-<range-end>, <range-start>-<range-end>, <range-start>-<range-end>
-
unit
: единица измерения, используемая для запроса диапазона, обычно байты. -
<range-start>
: целое число, представляющее начальное значение диапазона в указанных единицах измерения. -
<range-end>
: целое число, представляющее конечное значение диапазона в указанных единицах измерения.Это значение является необязательным и, если его нет, указывает, что область действия распространяется до конца документа.
пониматьRange
После синтаксиса давайте взглянем на реальный пример использования:
# 单一范围
$ curl http://i.imgur.com/z4d4kWk.jpg -i -H "Range: bytes=0-1023"
# 多重范围
$ curl http://www.example.com -i -H "Range: bytes=0-50, 100-150"
9.1 Интерфейсный код
html
<h3>范围下载示例</h3>
<button onclick="download()">下载</button>
js
async function download() {
try {
let rangeContent = await getBinaryContent(
"http://localhost:3000/file.txt",
0, 100, "text"
);
const blob = new Blob([rangeContent], {
type: "text/plain;charset=utf-8",
});
saveAs(blob, "hello.txt");
} catch (error) {
console.error(error);
}
}
function getBinaryContent(url, start, end, responseType = "arraybuffer") {
return new Promise((resolve, reject) => {
try {
let xhr = new XMLHttpRequest();
xhr.open("GET", url, true);
xhr.setRequestHeader("range", `bytes=${start}-${end}`);
xhr.responseType = responseType;
xhr.onload = function () {
resolve(xhr.response);
};
xhr.send();
} catch (err) {
reject(new Error(err));
}
});
}
когда пользователь нажимаетскачатькнопка, она будет вызыватьсяdownload
функция. Внутри этой функции она будет вызыватьсяgetBinaryContent
функция для инициирования запроса диапазона. Соответствующее сообщение HTTP-запроса выглядит следующим образом:
GET /file.txt HTTP/1.1
Host: localhost:3000
Connection: keep-alive
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36
Accept: */*
Accept-Encoding: identity
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,id;q=0.7
Range: bytes=0-100
Когда сервер получает запрос диапазона, он возвращает соответствующее ответное сообщение HTTP:
HTTP/1.1 206 Partial Content
Vary: Origin
Access-Control-Allow-Origin: null
Accept-Ranges: bytes
Last-Modified: Fri, 09 Jul 2021 00:17:00 GMT
Cache-Control: max-age=0
Content-Type: text/plain; charset=utf-8
Date: Sat, 10 Jul 2021 02:19:39 GMT
Connection: keep-alive
Content-Range: bytes 0-100/2590
Content-Length: 101
Из приведенного выше ответного сообщения HTTP мы видим ранее представленный206код состояния иAccept-Rangesстолица. Кроме того, поContent-Range
Во-первых, мы знаем общий размер файла. После успешного получения тела ответа на запрос диапазона мы можем использовать возвращенное содержимое в качестве параметра, вызвать конструктор BLOB-объектов для создания соответствующего объекта BLOB-объектов, а затем использовать метод saveAs, предоставляемый библиотекой FileSaver, для загрузки файла.
9.2 Код сервера
const Koa = require("koa");
const cors = require("@koa/cors");
const serve = require("koa-static");
const range = require("koa-range");
const PORT = 3000;
const app = new Koa();
// 注册中间件
app.use(cors());
app.use(range);
app.use(serve("."));
app.listen(PORT, () => {
console.log(`应用已经启动:http://localhost:${PORT}/`);
});
Код на стороне сервера относительно прост, а запрос диапазона осуществляется черезkoa-rangeпромежуточное ПО для достижения. Из-за ограниченного места Брат Абао не будет его представлять. Заинтересованные друзья, вы можете прочитать промежуточное программное обеспечение самостоятельноисходный код. На самом деле запросы диапазона также могут применяться к сценариям загрузки больших файлов.Если файловый сервер поддерживает запросы диапазона, когда клиент загружает большие файлы, он может рассмотреть возможность использования схемы загрузки больших файловых блоков.
Пример загрузки области:range
10. Скачивайте большие файлы по частям
Я считаю, что некоторые мелкие партнеры уже поняли решение для загрузки больших файлов.При загрузке больших файлов, чтобы повысить эффективность загрузки, мы обычно используемBlob.sliceМетод обрезает большой файл в соответствии с указанным размером, а затем запускает многопоточную загрузку по частям. После успешной загрузки всех частей он уведомляет сервер о необходимости их объединения.
Итак, можем ли мы принять аналогичную идею для загрузки больших файлов? На самом деле сервер поддерживаетRange
При условии заголовка запроса мы также можем реализовать функцию загрузки больших файлов блоками, конкретная схема обработки показана на следующем рисунке:
Потому чтоКак реализовать одновременную загрузку больших файлов в JavaScript?В этой статье Брат Абао уже подробно представил схему одновременной загрузки больших файлов, поэтому я не буду ее здесь представлять. Мы рассматриваем только полный процесс одновременной загрузки больших файлов:
На самом деле, в большом сценарии загрузки файлов блоков мы использовалиasync-pool这个库来实现并发控制。该库提供了 ES7 和 ES6 两种不同版本的实现,代码很简洁优雅。 Если вы хотите знатьasync-poolКак реализовать параллельный контроль, вы можете прочитатьКак реализовать контроль параллелизма в JavaScript?Эта статья.
Пример загрузки больших файлов кусками:big-file
11. Резюме
В этой статье брат Абао подробно описывает сценарии загрузки файлов 9. Я надеюсь, что после прочтения этой статьи у вас будет определенное понимание технологий, лежащих в основе сценариев 9. На самом деле, в процессе передачи файлов, чтобы повысить эффективность передачи, мы можем использоватьgzip
,deflate
илиbr
Сжимает файл с помощью алгоритма сжатия, такого как этот. Из-за ограниченного места брат Абао не будет его представлять, если вам интересно, вы можете прочитатьНесколько схем HTTP-передачи больших файловЭта статья.
С сценой загрузки файла, как можно не загрузить место загрузки файла? Если вы еще не читалиЗагрузка файла, достаточно познакомиться с этими восемью сценамиЭту статью я предлагаю вам прочитать вместе, когда у вас будет время. Еще раз спасибо за вашу постоянную поддержку.Если вы хотите узнать больше о содержании, пожалуйста, оставьте сообщение брату Абао.
12. Справочные ресурсы
- MDN — показатьSaveFilePicker
- MDN — Content-Disposition
- The File System Access API: simplifying access to local files
- Reading and writing files and directories with the browser-fs-access library
- Загрузка файлов, достаточно понять эти 8 сценариев
- Как реализовать одновременную загрузку больших файлов в JavaScript?