FileSaver.jsРешение для сохранения файлов на стороне клиента, идеально подходящее для веб-приложений, генерирующих файлы на стороне клиента. Он прост в использовании и совместим с большинством браузеров и используется в качестве зависимости в 63 000 проектов. В недавнем проекте брат Абао снова использовал его, поэтому я хотел написать статью, чтобы рассказать об этом прекрасном проекте с открытым исходным кодом.
1. Введение в FileSaver.js
FileSaver.jsЯвляется реализацией HTML5 saveAs() FileSaver. Он поддерживает большинство основных браузеров, и его совместимость показана на следующем рисунке:
(Источник изображения:GitHub.com/Eli GRE собирается/FI…
Следуйте «Дороге полного совершенствования», чтобы прочитать 3 бесплатные электронные книги (в общей сложности более 20 000 загрузок) и более 50 учебных пособий «Повторное изучение TS» от брата Абао.
1.1 saveAs API
FileSaver saveAs(Blob/File/Url, optional DOMString filename, optional Object { autoBom })
Метод saveAs поддерживает три параметра, первый параметр указывает, что он поддерживаетBlob/File/Url
Три типа, второй параметр представляет имя файла (необязательно), а третий параметр представляет объект конфигурации (необязательно). если тебе нужноFlieSaver.jsПодсказки кодировки текста Unicode предоставляются автоматически (ссылка:знак порядка байтов), необходимо установить{ autoBom: true}
.
1.2 Сохранить текст
let blob = new Blob(["大家好,我是阿宝哥!"], { type: "text/plain;charset=utf-8" });
FileSaver.saveAs(blob, "hello.txt");
1.3 Сохранить онлайн-ресурсы
FileSaver.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…
1.4 Сохранение содержимого холста Canvas
let canvas = document.getElementById("my-canvas");
canvas.toBlob(function(blob) {
saveAs(blob, "abao.png");
});
должен быть в курсеcanvas.toBlob()
метод доступен не во всех браузерах, для этой проблемы вы можете использоватьcanvas-toBlob.jsдля решения проблем совместимости.
(Источник изображения:потрите newser.com/?search=to B…
В приведенных выше примерах мы видели Blob много раз, поэтому, представляя исходный код FileSaver.js, брат Бао кратко представит соответствующие знания о Blob.
2. Введение в блоб
Blob (Binary Large Object) представляет собой большой объект двоичного типа. В системе управления базами данных двоичные данные хранятся как набор одного объекта. Блобы обычно представляют собой изображения, звуковые или мультимедийные файлы.Объекты типа Blob в JavaScript представляют собой неизменяемые файловые объекты необработанных данных.
2.1 Конструктор больших двоичных объектов
Blob
опциональной строкойtype
(обычно тип MIME) иblobParts
сочинение:
MIME (многоцелевые расширения почты Интернета) — это тип многоцелевого расширения почты Интернета, который представляет собой способ установить файл с определенным расширением для открытия приложением.При доступе к файлу расширения браузер будет автоматически использовать указанный расширение приложение для открытия. Он в основном используется для указания некоторых определяемых клиентом имен файлов, а также некоторых методов открытия медиафайлов.
Общие типы MIME: язык гипертекстовой разметки text .html text/html, изображение PNG .png image/png, обычный текст .txt text/plain и т. д.
В JavaScript мы можем создавать объекты Blob с помощью конструктора Blob Синтаксис конструктора Blob следующий:
var aBlob = new Blob(blobParts, options);
Соответствующие параметры описываются следующим образом:
- blobParts: это массив объектов, таких как ArrayBuffer, ArrayBufferView, Blob, DOMString и т. д. DOMStrings будут закодированы как UTF-8.
- options: необязательный объект, содержащий следующие два свойства:
- тип - значение по умолчанию
""
, который представляет тип MIME содержимого массива, которое будет помещено в большой двоичный объект. - окончания - значение по умолчанию
"transparent"
, используется для указания окончания строки, включающей\n
как пишется строка. Это одно из следующих двух значений:"native"
, что означает, что символ конца строки заменяется символом новой строки, подходящим для файловой системы основной операционной системы, или"transparent"
, что означает, что терминатор, сохраненный в большом двоичном объекте, останется неизменным.
- тип - значение по умолчанию
Теперь, когда мы рассмотрели большие двоичные объекты, давайте поговорим об URL-адресах больших двоичных объектов.
2.2 Blob URL
URL-адрес BLOB-объекта/URL-адрес объекта — это псевдопротокол, который позволяет использовать объекты Blob и File в качестве источников URL-адресов для изображений, ссылок для загрузки двоичных данных и многого другого. В браузере мы используемURL.createObjectURL
метод создания URL-адреса BLOB-объекта, который принимаетBlob
объекта и создать для него уникальный URL видаblob:<origin>/<uuid>
, соответствующий пример выглядит следующим образом:
blob:https://example.org/40a5fb5a-d56d-4a33-b4e2-0acf6a8e5f641
Внутри браузера для каждого проходаURL.createObjectURL
Сгенерированный URL-адрес сохраняет URL-адрес → Карта BLOB-объектов. Поэтому такие URL короче, но к ним можно получить доступBlob
. Сгенерированный URL-адрес действителен только в том случае, если текущий документ открыт. это позволяет цитировать<img>
,<a>
серединаBlob
, но если вы перейдете по URL-адресу большого двоичного объекта, которого больше не существует, вы получите ошибку 404 в своем браузере.
Приведенный выше URL-адрес большого двоичного объекта выглядит великолепно, но на самом деле он имеет побочные эффекты.Хотя сопоставление URL-адрес → BLOB-объект сохраняется, сам BLOB-объект все еще находится в памяти, и браузер не может его освободить. Сопоставление автоматически очищается, когда документ выгружается, поэтому объект Blob затем освобождается.. Однако, если приложение является долгоживущим, это не произойдет в ближайшее время. Таким образом, если мы создадим URL-адрес большого двоичного объекта, он будет существовать в памяти, даже если этот большой двоичный объект больше не нужен.
Для этой проблемы мы можем вызватьURL.revokeObjectURL(url)
удаляет ссылку из внутренней карты, позволяя удалить большой двоичный объект, если нет других ссылок, и освобождает память.
Хорошо, теперь мы рассмотрели большие двоичные объекты и URL-адреса больших двоичных объектов. Если вы все еще не имеете ни малейшего представления и хотите глубже понять Blob, вы можете прочитатьКапли, о которых вы не зналиВ этой статье мы начнем анализировать исходный код FileSaver.js.
Если вы хотите узнать идеи и навыки чтения исходного кода, вы можете прочитатьИспользуя эти идеи и методы, я прочитал множество отличных проектов с открытым исходным кодом.Эта статья.
В-третьих, анализ исходного кода FileSaver.js.
В FileSaver.js есть три схемы для реализации сохранения файлов, поэтому мы представим эти три схемы отдельно.
3.1 Вариант 1
Когда FileSaver.js сохраняет файл, если тег a на текущей платформе поддерживаетdownload
свойств, а не в среде MacOS WebView, он будет иметь приоритетa[download]
чтобы сохранить файл. В процессе конкретного использования мы называемsaveAs
способ сохранения файла, метод определяется следующим образом:
FileSaver saveAs(Blob/File/Url, optional DOMString filename, optional Object { autoBom })
Наблюдая за сигнатурой метода saveAs, мы знаем, что метод поддерживает два типа параметров: string и Blob, поэтому эти два типа параметров необходимо обрабатывать отдельно в методе saveAs.Давайте сначала проанализируем ситуацию со строковыми параметрами.
3.1.1 Параметры строкового типа
В предыдущем примере мы продемонстрировали, как использоватьsaveAs
Способы сохранения изображений онлайн:
FileSaver.saveAs("https://httpbin.org/image", "image.jpg");
В схеме один,saveAs
Логика обработки метода следующая:
// Use download attribute first if possible (#193 Lumia mobile) unless this is a macOS WebView
function saveAs(blob, name, opts) {
var URL = _global.URL || _global.webkitURL;
var a = document.createElement("a");
name = name || blob.name || "download";
a.download = name;
a.rel = "noopener";
if (typeof blob === "string") {
a.href = blob;
if (a.origin !== location.origin) { // (1)
corsEnabled(a.href)
? download(blob, name, opts)
: click(a, (a.target = "_blank"));
} else { // (2)
click(a);
}
} else {
// 省略处理Blob类型参数
}
}
В приведенном выше коде, если обнаружится, что URL-адрес загруженного ресурса не находится в том же домене, что и текущий сайт, он будет использоваться первым.Синхронизированный запрос HEADопределить, поддерживать лиCORSмеханизм, если он поддерживается, вызоветdownload
способ скачивания файлов. Сначала проанализируемcorsEnabled
метод:
function corsEnabled(url) {
var xhr = new XMLHttpRequest();
xhr.open("HEAD", url, false);
try {
xhr.send();
} catch (e) {}
return xhr.status >= 200 && xhr.status <= 299;
}
corsEnabled
Реализация метода очень проста, т.XMLHttpRequest
API инициирует синхронный запрос HEAD, а затем определяет, находится ли возвращенный код состояния в[200 ~ 299]В диапазоне. Тогда давайте посмотримdownload
Конкретная реализация метода:
function download(url, name, opts) {
var xhr = new XMLHttpRequest();
xhr.open("GET", url);
xhr.responseType = "blob";
xhr.onload = function () {
saveAs(xhr.response, name, opts);
};
xhr.onerror = function () {
console.error("could not download file");
};
xhr.send();
}
такой жеdownload
Реализация метода также очень проста, в том числе за счетXMLHttpRequestЗнакомый всем API для инициации HTTP-запросовJSONФормат другой, нам нужно установитьresponseType
имеет типblob
. Кроме того, поскольку возвращаемый результат представляет собой данные типа blob, он будет продолжать вызываться внутри функции обратного вызова успеха.saveAs
метод сохранения файла.
А для случая, когда не поддерживается механизм CORS или тот же домен, он вызовет внутреннийclick
способ завершения функции загрузки, конкретная реализация этого метода выглядит следующим образом:
// `a.click()` doesn't work for all browsers (#465)
function click(node) {
try {
node.dispatchEvent(new MouseEvent("click"));
} catch (e) {
var evt = document.createEvent("MouseEvents");
evt.initMouseEvent(
"click", true, true, window, 0, 0, 0, 80, 20,
false, false, false, false, 0, null
);
node.dispatchEvent(evt);
}
}
существуетclick
Внутри метода сначала будет вызываться метод объекта node.dispatchEvent
способ отправкиclick
событие. Когда возникает исключение, оно будетcatch
Оператор выполняет соответствующую обработку исключений,catch
в предложенииMouseEvent.initMouseEvent()Метод используется для инициализации значения события мыши.Но следует отметить, что эта функция была удалена из веб-стандарта, хотя некоторые браузеры все еще поддерживают ее в настоящее время, но могут перестать поддерживать ее в будущем, пожалуйста, постарайтесь не использовать эту функцию..
3.1.2 параметры типа BLOB-объекта
Опять же, в предыдущем примере мы продемонстрировали, как использоватьsaveAs
способ сохранения данных типа Blob:
let blob = new Blob(["大家好,我是阿宝哥!"], { type: "text/plain;charset=utf-8" });
FileSaver.saveAs(blob, "hello.txt");
Логика обработки параметров типа blob определена вsaveAs
В другой ветке тела метода:
// Use download attribute first if possible (#193 Lumia mobile) unless this is a macOS WebView
function saveAs(blob, name, opts) {
var URL = _global.URL || _global.webkitURL;
var a = document.createElement("a");
name = name || blob.name || "download";
a.download = name;
a.rel = "noopener";
if (typeof blob === "string") {
// 省略处理字符串类型参数
} else {
a.href = URL.createObjectURL(blob);
setTimeout(function () {
URL.revokeObjectURL(a.href);
}, 4e4); // 40s
setTimeout(function () {
click(a);
}, 0);
}
}
Для параметров типа blob первый проходcreateObjectURL
метод для создания URL-адреса объекта, а затем передатьclick
Метод выполняет сохранение файла. Чтобы вовремя освободить память, в ветке обработки else запускается таймер для выполнения операции очистки. На данный момент мы уже представили первое решение, а второе решение, которое будет представлено далее, в основном предназначено для совместимости с браузерами IE.
3.2 Вариант 2
В браузерах Internet Explorer 10 методы msSaveBlob и msSaveOrOpenBlob позволяют пользователям сохранять файлы на клиенте, где метод msSaveBlob предоставляет только кнопку сохранения, аmsSaveOrOpenBlob
Метод предоставляет кнопки сохранения и открытия, и соответствующее использование выглядит следующим образом:
window.navigator.msSaveBlob(blobObject, 'msSaveBlob_hello.txt');
window.navigator.msSaveOrOpenBlob(blobObject, 'msSaveBlobOrOpenBlob_hello.txt');
После понимания вышеуказанных знаний и решений, представленных на схеме 1corsEnabled
,download
иclick
После метода посмотрите код второй схемы, он очень понятен. в удовлетворении"msSaveOrOpenBlob" in navigator
Когда условия соблюдены, FileSaver.js будет использовать вторую схему для сохранения файлов. Как и прежде, давайте проанализируемПараметр строкового типалогика обработки.
3.2.1 Параметры строкового типа
// Use msSaveOrOpenBlob as a second approach
function saveAs(blob, name, opts) {
name = name || blob.name || "download";
if (typeof blob === "string") {
if (corsEnabled(blob)) { // 判断是否支持CORS
download(blob, name, opts);
} else {
var a = document.createElement("a");
a.href = blob;
a.target = "_blank";
setTimeout(function () {
click(a);
});
}
} else {
// 省略处理Blob类型参数
}
}
3.2.2 параметры типа BLOB-объекта
// Use msSaveOrOpenBlob as a second approach
function saveAs(blob, name, opts) {
name = name || blob.name || "download";
if (typeof blob === "string") {
// 省略处理字符串类型参数
} else {
navigator.msSaveOrOpenBlob(bom(blob, opts), name); // 提供了保存和打开按钮
}
}
3.3 Вариант 3
Если ни вариант 1, ни вариант 2 не поддерживаются, FileSaver.js будет понижен до использованияFileReaderAPI иopenAPI для открытия нового окна для сохранения файлов.
3.3.1 Параметры строкового типа
// Fallback to using FileReader and a popup
function saveAs(blob, name, opts, popup) {
// Open a popup immediately do go around popup blocker
// Mostly only available on user interaction and the fileReader is async so...
popup = popup || open("", "_blank");
if (popup) {
popup.document.title = popup.document.body.innerText = "downloading...";
}
if (typeof blob === "string") return download(blob, name, opts);
// 处理Blob类型参数
}
3.3.2 параметры типа BLOB-объекта
Для параметров типа blob в методе saveAs будут выбраны разные схемы в соответствии с разными средами.Например, в среде браузера Safari он будет использоватьFileReaderAPI сначала преобразует объект Blob в URL-адрес данных, а затем назначает URL-адрес данных вновь открытому окну или текущему окну.location
объект, конкретный код выглядит следующим образом:
// Fallback to using FileReader and a popup
function saveAs(blob, name, opts, popup) {
// Open a popup immediately do go around popup blocker
// Mostly only available on user interaction and the fileReader is async so...
popup = popup || open("", "_blank");
if (popup) { // 设置新开窗口的标题
popup.document.title = popup.document.body.innerText = "downloading...";
}
if (typeof blob === "string") return download(blob, name, opts);
var force = blob.type === "application/octet-stream"; // 二进制流数据
var isSafari = /constructor/i.test(_global.HTMLElement) || _global.safari;
var isChromeIOS = /CriOS\/[\d]+/.test(navigator.userAgent);
if (
(isChromeIOS || (force && isSafari) || isMacOSWebView) &&
typeof FileReader !== "undefined"
) {
// Safari doesn't allow downloading of blob URLs
var reader = new FileReader();
reader.onloadend = function () {
var url = reader.result;
url = isChromeIOS
? url
: url.replace(/^data:[^;]*;/, "data:attachment/file;"); // 处理成附件的形式
if (popup) popup.location.href = url;
else location = url;
popup = null; // reverse-tabnabbing #460
};
reader.readAsDataURL(blob);
} else {
// 省略Object URL的处理逻辑
}
}
На самом деле дляFileReaderAPI, помимо поддержки преобразования объектов File/Blob в URL-адреса данных, он также обеспечиваетreadAsArrayBuffer()
иreadAsText()
Методы преобразования объектов File/Blob в другие форматы данных. существуетИграйте с интерфейсным двоичным кодомВ статье брат Абао подробно представилFileReaderПрименение API во внешних сценариях обработки изображений, прочитав эту статью, вы сможете легко понять следующие диаграммы преобразования:
Наконец, давайте посмотрим на код ветки else:
function saveAs(blob, name, opts, popup) {
popup = popup || open("", "_blank");
if (popup) {
popup.document.title = popup.document.body.innerText = "downloading...";
}
// 处理字符串类型参数
if (typeof blob === "string") return download(blob, name, opts);
if (
(isChromeIOS || (force && isSafari) || isMacOSWebView) &&
typeof FileReader !== "undefined"
) {
// 省略FileReader API处理逻辑
} else {
var URL = _global.URL || _global.webkitURL;
var url = URL.createObjectURL(blob);
if (popup) popup.location = url;
else location.href = url;
popup = null; // reverse-tabnabbing #460
setTimeout(function () {
URL.revokeObjectURL(url);
}, 4e4); // 40s
}
}
На данный момент исходный код библиотеки FileSaver.js был проанализирован.Прочитав приведенный выше исходный код с Brother Abao, вы думаете, что написать совместимую и простую в использовании стороннюю библиотеку нелегко? В реальном проекте, если вам нужно сохранить очень большие файлы, которые превышают ограничение размера большого двоичного объекта, или если недостаточно места в памяти, вы можете рассмотреть возможность использования более продвинутогоStreamSaver.jsбиблиотека для реализации функции сохранения файлов.
Следуйте «Дорога к бессмертному совершенствованию с полным стеком», чтобы прочитать 3 бесплатные электронные книги (всего более 20 000 загрузок) и 8 серий руководств по анализу исходного кода, изначально написанных братом А. Бао.