⚠️ Эта статья является первой подписанной статьей сообщества Nuggets, и ее перепечатка без разрешения запрещена.
существует7 лет технического письма, делюсь 6 опытомВ этой статье Brother A Bao представляет хороший онлайн-инструмент для рисования, который он часто использует:Excalidraw. С его помощью вы можете легко рисовать всевозможные красивые схемы, нарисованные от руки, в настоящее время на Github.Excalidrawдостиг количества звезд22.7K, так что это также очень хороший проект с открытым исходным кодом.
при обычном использованииExcalidrawВ то время Baoge обнаружил, что онлайн-инструмент предоставляет несколько полезных функций. например сохранить *.excalidrawФайл в указанный каталог, перетащите, чтобы открыть *.excalidrawФайлируйте и сохраняйте в текущий файл, копируйте изображения в буфер обмена, делитесь ссылками только для чтения и сотрудничайте в режиме реального времени.
Совет: на изображении выше показано перетаскивание, чтобы открыть *.excalidrawФайлы и сохранить в текущий файл
Многие из вышеперечисленных функций связаны с файловыми операциями. По поводу обработки файлов брат Абао писал ранееЗагрузка файлов, достаточно понять эти 8 сценариева такжеЗагрузка файла, достаточно понять эти 9 сценариевэти две статьи. И третья статья, брат А Бао возьмет вас на анализExcalidrawТехнология, лежащая в основе этого, связана с манипулированием файлами.
После понимания и освоения этих связанных технологий это может быть полезно в будущей работе, особенно для некоторых сценариев онлайн-веб-редактора, использование этих технологий значительно улучшит взаимодействие с пользователем продукта. Например, на платформах, поддерживающих соответствующие веб-технологии, разработанный вами онлайн-редактор может прекрасно поддерживатьОткрыть -> Изменить -> СохранитьЭто общий поток обработки файлов.
Без лишних слов, давайте сразу к делу, здесь мы сначала разберемСохраните файл .excalidraw в указанный каталог.функция.
1. Сохраните файл в указанный каталог
Совет. Во всех демонстрационных примерах в этой статье используется версия Chrome: версия 92.0.4515.159 (официальная версия) (x86_64).
Приведенная выше Gif-анимация демонстрирует процесс сохранения файла в указанную директорию, т.к.ExcalidrawЭтот онлайн-инструмент имеет открытый исходный код, поэтому, анализируя егоисходный код, находим реализациюСохраните файл в указанную директориюФункция реализации функции:
// https://github.com/excalidraw/excalidraw/blob/master/src/data/json.ts#L31
import { fileOpen, fileSave } from "browser-fs-access";
export const saveAsJSON = async (
elements: readonly ExcalidrawElement[],
appState: AppState,
) => {
const serialized = serializeAsJSON(elements, appState);
const blob = new Blob([serialized], {
type: MIME_TYPES.excalidraw,
});
const fileHandle = await fileSave(
blob,
{
fileName: `${appState.name}.excalidraw`,
description: "Excalidraw file",
extensions: [".excalidraw"],
},
isImageFileHandle(appState.fileHandle) ? null : appState.fileHandle,
);
return { fileHandle };
};
Как видно из приведенного выше кода, вsaveAsJSON
Внутри функции вызываетсяfileSave
функция сохранения файла.fileSave
функция изbrowser-fs-accessЭта третья библиотека импортирована. Библиотека инкапсулируетFile_System_Access_API, который предоставляет разработчикамЧтение, запись файлов и управление файламиСпособность. а такжеСохраните файл в указанную директориюфункцию, черезshowSaveFilePickerметод достижения. существуетshowSaveFilePickerДо появления метода функция сохранения файлов была реализована на стороне клиента, более распространенным решением было использованиеэтикеткаилиFileSaver.jsэта библиотека.
const saveFile = async (blob, filename) => {
const a = document.createElement('a');
a.download = filename;
a.href = URL.createObjectURL(blob);
a.addEventListener('click', (e) => {
setTimeout(() => URL.revokeObjectURL(a.href), 30 * 1000);
});
a.click();
};
Совет: Если вы хотите узнать о других способах загрузки файлов, вы можете прочитатьЗагрузка файла, достаточно понять эти 9 сценариевЭта статья.
Для представленной выше схемы сохранения файлов на стороне клиента самая большая проблема заключается в том, что ее невозможно реализовать.Открыть -> Изменить -> СохранитьЭто общий процесс работы с файлами. Поскольку у нас нет возможности перезаписать исходный файл, мы можем только создать новый. и использовать новыйFile_System_Access_APIНапример, чтобы решить вышеуказанную проблему, мы можем использоватьwindow.showOpenFilePickerметод, чтобы открыть файл, и после того, как файл был отредактирован, используйтеwindow.showSaveFilePickerчтобы сохранить файл.
(адрес текстового редактора:Google Chrome labs.GitHub.IO/текстовый редактор…
Давайте представимshowSaveFilePickerАПИ, это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"],
},
},
],
});
const writable = await handle.createWritable();
await writable.write(blob);
await writable.close();
return handle;
} catch (err) {
console.error(err.name, err.message);
}
}
saveFile(imgBlob, "face.png");
когда вы используете вышеsaveFile
функция, при сохранении изображения отображается следующий селектор файла сохранения:
Смотри сюда, тебе не кажетсяshowSaveFilePickerФункция API довольно мощная, но, к сожалению, совместимость API в настоящее время не очень хорошая, как показано на следующем рисунке:
(Источник изображения:потрите newser.com/?search=yes о…
showSaveFilePickerдаFile System Accessметоды, определенные в API, за исключениемshowSaveFilePickerКроме того, естьshowOpenFilePickerа такжеshowDirectoryPickerи другие методы. Далее Brother Abao кратко представит два других полезных API.
showOpenFilePickerАПИ, этоWindow
Метод, определенный в интерфейсе, который при вызове отображает средство выбора файлов, позволяющее пользователю выбрать один или несколько файлов. Сигнатура метода выглядит так:
let FileSystemHandles = Window.showOpenFilePicker();
showOpenFilePickerМетод поддерживает необязательный параметр типа object, который может содержать следующие свойства:
-
multiple
: Логический тип, значение по умолчаниюfalse
. Если установленоtrue
, можно выбрать несколько файлов. -
excludeAcceptAllOption
: Логический тип, значение по умолчаниюfalse
. По умолчанию селектор должен содержать параметр, позволяющий не применять фильтры по типам файлов (как указано в следующемtypes
опция включена). Установите этот параметр наtrue
иметь в видуtypes
Варианты недоступны. -
types
: Тип массива, указывающий список типов файлов, которые разрешено сохранять. Каждый элемент массива представляет собой объект конфигурации, содержащий следующие свойства:-
description(可选)
: Используется для описания категории типов файлов, которые разрешено сохранять. -
accept
: это объект, чейkey
даMIMEТип, значение — список расширений файлов.
-
передачаshowOpenFilePickerПосле метода он вернетFileSystemHandles
которыйFileSystemFileHandleмассив объектов. имеютFileSystemFileHandleобъект, вы можете затем вызывать методы этого объекта для управления файлом. Возьмем простой пример использования:
<div>
<textarea id="container" rows="5" cols="30"></textarea>
</div>
<button onclick="openFile()">打开文件</button>
<script>
const container = document.querySelector("#container");
async function openFile() {
let [fileHandle] = await window.showOpenFilePicker();
const file = await fileHandle.getFile();
const contents = await file.text();
container.value = contents;
}
</script>
В приведенном выше примере, когда пользователь нажимаетоткрыть файлкнопка, отображается селектор файлов. После выбора текстового файла содержимое файла будет отображаться вtextarea#containerтекстовое окно. Для нетекстовых файлов это можно сделать, вызвавarrayBufferметод для чтения двоичного содержимого файла.
(Источник изображения:потрите newser.com/?search=yes о…
Как видно из приведенного выше рисунка, токshowOpenFilePickerСовместимость API по-прежнему относительно плохая. но если вы хотите поддержатьFile System AccessНа платформе API, если вы отдаете приоритет использованию этих API, вы можете рассмотреть возможность использованияGoogleChromeLabsОткрытый исходный кодbrowser-fs-accessэта библиотека, которая позволяет вам поддерживатьFile System AccessПроще использовать на платформе APIFile System AccessAPI, в то время как для неподдерживаемых платформ он будет автоматически понижен до использования<input type="file">
а также<a download>
Путь.
Помимо выбора файлов, мы также можем выбирать каталоги. Для этого сценария мы можем использоватьshowDirectoryPickerAPI. этоWindow
接口中定义的方法,调用该方法后会显示一个允许用户选择目录的选择器。 Сигнатура метода выглядит так:
var FileSystemDirectoryHandle = Window.showDirectoryPicker();
с ранее описаннымshowOpenFilePickerРазница в том, что вызовshowDirectoryPickerПосле того, как метод, возвратFileSystemDirectoryHandleобъект. Используя этот объект, мы можем выполнять некоторые операции, связанные с каталогом. Например, прочитать информацию о каталоге, прочитать указанный файл в каталоге, удалить указанный файл в каталоге или создать новый файл в каталоге. Опять же, давайте возьмем несколько простых примеров.
читать информацию каталога
async function readDirectory() {
const dirHandle = await window.showDirectoryPicker();
for await (const entry of dirHandle.values()) {
console.log(entry.kind, entry.name);
}
}
Прочитать указанный файл в каталоге
const container = document.querySelector("#container");
async function readFile() {
const dirHandle = await window.showDirectoryPicker();
const fileHandle = await dirHandle.getFileHandle("hello.txt");
const file = await fileHandle.getFile();
const contents = await file.text();
container.value = contents;
}
Удалить указанный файл в каталоге
async function removeFile() {
const dirHandle = await window.showDirectoryPicker();
const result = await dirHandle.removeEntry("hello.copy.txt");
container.value = `删除hello.copy.txt文件${
typeof result == "undefined" ? "成功" : "失败"
}`;
}
должны знать о том,removeEntryВ дополнение к поддержке удаления указанного файла метод также может поддерживать удаление указанного каталога.
Создать указанный файл
async function createFile() {
const dirHandle = await window.showDirectoryPicker();
const fileHandle = await dirHandle.getFileHandle("hello.new.txt", {
create: true,
});
container.value = "hello.new.txt文件创建成功!";
const writable = await fileHandle.createWritable();
await writable.write(new Blob(["大家好,我是阿宝哥!"]));
await writable.close();
}
В приведенном выше коде мы вызываемgetFileHandle
метод получения указанного файла, соответствующийFileSystemFileHandleобъект.create: true
Указывает, что если указанный файл не найден в текущем каталоге, будет создан новый файл. После понимания приведенных выше примеров чувствуете ли вы, что возможности браузера по обработке файлов становятся все сильнее и сильнее. Аналогично, давайте посмотрим наshowDirectoryPickerСовместимость с API:
(Источник изображения:потрите newser.com/?search=yes о…
2. Перетащите, чтобы открыть *.excalidrawфайл и сохранить в текущий файл
Приведенная выше анимация Gif демонстрирует перетаскивание, чтобы открыть *.excalidrawВ процессе сохранения файла в текущий файл можно обнаружить, что после редактирования файла нам нужно только подтвердить, следует ли сохранять файл, не выбирая путь сохранения файла, что значительно улучшает взаимодействие с пользователем.
class App extends React.Component<AppProps, AppState> {
// 省略大部分代码
const file = event.dataTransfer?.files[0];
if (
file?.type === MIME_TYPES.excalidrawlib ||
file?.name?.endsWith(".excalidrawlib")
) {
// 处理导入的控件库的逻辑
} else {
this.setState({ isLoading: true });
if (fsSupported) { // 判断是否支持File System Access API
try {
const item = event.dataTransfer.items[0];
// 关键点:获取FileSystemHandle对象
(file as any).handle = await (item as any).getAsFileSystemHandle();
} catch (error) {
console.warn(error.name, error.message);
}
}
// 加载.excalidraw文件到Canvas
await this.loadFileToCanvas(file);
}
};
}
Ключевым моментом приведенного выше кода является то, что вызовDataTransferItem.getAsFileSystemHandle()
способ получитьFileSystemFileHandleобъект. Получив этот объект, мы можем читать и писать в файл. Конкретное использование заключается в следующем:
Пример чтения файла
async function getTheFile() {
// open file picker
[fileHandle] = await window.showOpenFilePicker(pickerOpts);
// get file contents
const fileData = await fileHandle.getFile();
}
пример файла записи
async function writeFile(fileHandle, contents) {
// Create a FileSystemWritableFileStream to write to.
const writable = await fileHandle.createWritable();
// Write the contents of the file to the stream.
await writable.write(contents);
// Close the file and write the contents to disk.
await writable.close();
}
3. Скопируйте изображение в буфер обмена
// https://github.com/excalidraw/excalidraw/blob/master/src/clipboard.ts
export const copyBlobToClipboardAsPng = async (blob: Blob) => {
await navigator.clipboard.write([
new window.ClipboardItem({ "image/png": blob }),
]);
};
В приведенном выше кодеcopyBlobToClipboardAsPng
функция поддерживаетblobпараметр, который будет вызываться внутри функцииnavigator.clipboard.write
метод копирования изображения в буфер обмена. А для обычного текста можно пройтиnavigator.clipboard.writeText
способ записи их в системный буфер обмена.
фактическиnavigator.clipboard.write
а такжеnavigator.clipboard.writeText
путьClipboard
методы, определенные интерфейсом, реализующимClipboardAPI, если пользователь предоставляет соответствующие разрешения, предоставляет доступ для чтения и записи к системному буферу обмена. В веб-приложении API буфера обмена можно использовать для реализации функций вырезания, копирования и вставки. Этот API используется для заменыdocument.execCommandAPI для реализации операций с буфером обмена.
В реальной работе нам не нужно вручную создаватьClipboard
объект, но черезnavigator.clipboard
получитьClipboard
Объект:
в полученииClipboard
После объекта мы можем использовать API, предоставляемый объектом, для доступа к буферу обмена. Например, поnavigator.clipboard.readText
способ чтения содержимого буфера обмена:
navigator.clipboard.readText().then(
clipText => document.querySelector(".editor").innerText = clipText
);
Приведенный выше код будет включать в себя HTML.editor
Содержимое первого элемента класса заменяется содержимым буфера обмена. Если буфер обмена пуст или не содержит текста, содержимое элемента будет очищено. Это связано с тем, что когда буфер обмена пуст или не содержит текста,readText
Метод возвращает пустую строку.
API асинхронного буфера обмена — это относительно новый API, и браузеры все еще постепенно внедряют его. Из-за потенциальных проблем с безопасностью и технической сложности большинство браузеров постепенно интегрируют этот API. В настоящее времяNavigator API: clipboardСовместимость показана на следующем рисунке:
(Источник изображения:потрите newser.com/madonna-api_thatv…
Для расширений браузера вы можете запроситьclipboardReadа такжеclipboardWriteРазрешение на использование clipboard.readText() и clipboard.writeText(). Если вас интересуют другие API буфера обмена, вы можете прочитатьХотите скопировать изображение? Взгляните на API буфера обменаЭта статья.
На самом деле, помимо методов, описанных выше,ExcalidrawДругие веб-API также используются для реализации определенных функций. такие как использованиеwindow.cryptoКогда API используется для экспорта ссылок только для чтения, данные холста шифруются и защищаются. использоватьWebSocketAPI для совместного редактирования и использованияShareВ API реализована функция обмена файлами, заинтересованные партнеры могут прочитатьExcalidrawсоответствующий исходный код.
4. Резюме
В этой статье брат Абао анализируетExcalidrawЭтот онлайн-инструмент рисования, предоставил некоторые приятные функции для использования технологий. Надеюсь, закончите чтение этой статьи, выFile_System_Access_APIопределено вwindow.showOpenFilePicker,window.showSaveFilePicker,window.showDirectoryPickerа такжеDataTransferItem.getAsFileSystemHandleЭти методы имеют определенное понимание.
Из-за текущегоFile_System_Access_APIСовместимость не очень, если хотите использовать в проекте скажем. Мы предлагаем вам использоватьGoogleChromeLabsОткрытый исходный кодbrowser-fs-accessЭта библиотека, которая не только предоставляет нам более лаконичный API, но и предоставляет схему автоматического перехода на более раннюю версию. В будущих проектах, если у вас есть возможность, вы можете попробовать это.