前言
因 Unicode IDE 编辑器导入文件、文件夹需要,研究了下导入文件/文件夹的功能实现,发现现在相干文章有点少,故而记录下过程,如果有误,还望指正。(API的兼容性及相干属性、接口界说,请自行查看文件体系 API)
文件 API
文件 API 使得 web 应用可以访问文件和其中的内容,当我们想要访问一个文件,可以通过[](https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/input/file “”)大概拖放来实现。以这种方式提供的文件集被表示为FileList对象,这使得 web 应用能够检索单个File对象。
- const input = document.createElement("input");
- input.type = "file";
- input.multiple = true;
- input.addEventListener("change", () => {
- // 当然,此处也可以使用事件源 e 来获取 FileList 对象哈,e.target 指向 input
- const fileList = input.files;
- console.log("==> ", fileList);
- });
- input.click();
复制代码
Blob
Blob 代表“二进制大对象”(类似于文件的不可变的原始数据对象);Blob可以作为文本或二进制数据被读取,大概转换为ReadableStream。我们常用来处理处罚大文件数据分片,里面有一个Blob.slice()方法,用于获取指定字节范围内的数据。
- input.addEventListener("change", () => {
- // 当然,此处也可以使用事件源 e 来获取 FileList 对象哈,e.target 指向 input
- const fileList = input.files;
- // 以第一个 File 对象为例,获取 Blob
- const file = fileList![0];
- const blob = new Blob([file], { type: file.type });
- console.log("==> ", blob);
- });
复制代码
File
File对象提供对元数据的访问,如文件的名称、大小、类型和最后修改日期提供文件的信息,并答应网页中的 JavaScript 代码访问其中的内容,通常使用File举行内容读取、举行网络传输等。
FileReader
使 web 应用能够异步读取存储在用户计算机上的文件(或原始数据缓冲区)的内容,使用File或Blob对象来指定要读取的文件或数据。
通常,FileReader 是异步的,由于读取文件内容是需要时间的,因此,可以通过监听 load 实现读取乐成完成时回调处理处罚。读取文件还能将文件读成多种结果类型:
- input.addEventListener("change", () => {
- // 当然,此处也可以使用事件源 e 来获取 FileList 对象哈,e.target 指向 input
- const fileList = input.files;
- // 以第一个 File 对象为例,获取 Blob
- const file = fileList![0];
- const fileReader = new FileReader();
- fileReader.onload = () => {
- console.log(fileReader.result);
- };
- fileReader.readAsText(file);
- });
复制代码
Tips:关于上诉 FileReader、Blob、File等常见数据类型的界说、转换不在本篇的讨论范围哈,网上也有很具体的说明,各人搜索一下就可以了,我推荐两篇文章,各人可以看看~
谈谈JS二进制:File、Blob、FileReader、ArrayBuffer、Base64[这里是图片006]https://juejin.cn/post/7148254347401363463?searchId=20241113152541CCE5E6FEB74927A4CFEBjs二进制及其相干转换全总结(File、Blob、FileReader、ArrayBuffer、Base64、Object URL、DataURL…)[这里是图片007]https://juejin.cn/post/7395866692798201871?searchId=20241113152055D9FE2B4CE63230A35055
文件体系 API
用户直接操作体系文件黑白常危险的行为,因此,此API要求在安全上下文可用。
File System API 答应步伐与用户本地装备上的或是用户能够访问的网络文件体系上的文件举行交互。此 API 的焦点功能包括读取文件、写入或保存文件以及访问目录结构。大多数与文件和目录的交互都通过句柄来完成:
句柄是指在Web API中用于操作文件体系的接口或对象。
通过这些句柄,Web应用可以实现对本地文件体系的读取、写入、导航等操作。
File System API答应Web应用在用户的装备上创建、读取、导航用户本地文件体系中的沙盒部分,并向其中写入数据。
**FileSystemHandle**接口是代表一个文件或一个目录的对象,在大多数情况下,你不会直接使用FileSystemHandle,而是会用到它的FileSystemFileHandle和FileSystemDirectoryHandle子接口。
文件处理处罚
文件处理处罚最简朴的就是 input file了,这里就不演示了,**?FileSystemFileHandle**接口表示一个指向文件体系条目标句柄。可通过window.showOpenFilePicker()方法来访问此接口:
- try {
- // 通过文件选择器获取文件
- const file = await window.showOpenFilePicker();
- console.log("==> ", file);
- } catch (error) {
- console.error(error);
- }
复制代码
如上演示结果,尽管已经可以调用文件,但是 ts 照旧报错了,可以通过自界说 Window 的接口来解决这个问题,明白告诉window上存在这个方法即可,还可以拓展一些其他的属性,便于后期的操作哈~
window.showOpenFilePicker() 返回的结果是一个Promise<FileSystemFileHandle[]>是个数组,我们应该怎样获取该文件的具体内容呢?
读取文件内容
FileSystemFileHandle有一个getFile方法,用于返回一个 File 对象,有了这个对象,剩下的就是创建 FileReader 读取就可以啦
- try {
- // 通过文件选择器获取文件
- const fileListHandle = await window.showOpenFilePicker();
- fileListHandle.forEach(async (fileHandle) => {
- const file = await fileHandle.getFile();
- const fileReader = new FileReader();
- fileReader.readAsText(file);
- fileReader.onload = () => {
- const text = fileReader.result;
- console.log("==> 文件内容", text);
- };
- });
- } catch (error) {
- console.error(error);
- }
复制代码
写入文件
上面演示了怎样读取文件内容,那么,我们该怎样修改文件内容呢?createWritable()方法用于创建一个FileSystemWritableFileStream对象,可用于写入文件。
- try {
- // 通过文件选择器获取文件
- const fileListHandle = await window.showOpenFilePicker();
- // 拿第一个文件做演示哈
- const fileHandle = fileListHandle[0];
- // 创建写入句柄
- const writable = await fileHandle.createWritable();
- // 将文件内容写入到流中。
- await writable.write(`当前时间:${getDate()}`);
- // 关闭文件并将内容写入磁盘。
- await writable.close();
-
- console.log("==> 写入文件成功");
- } catch (error) {
- console.error(error);
- }
复制代码
还有同步写入的方法createSyncAccessHandle,这里就不演示了哈。如果写入的文件体系有权限要求,大概需要在获取句柄时,查询权限参数。
文件夹处理处罚
**FileSystemDirectoryHandle**接口提供指向一个文件体系目录的句柄。这个接口可以通过window.showDirectoryPicker()、
StorageManager.getDirectory()、
DataTransferItem.getAsFileSystemHandle()、
FileSystemDirectoryHandle.getDirectoryHandle()这些方法来获取。
因此,文件夹的处理处罚方式要多一些,FileSystemDirectoryHandle.getDirectoryHandle() 与第一个有重叠部分,因此本文仅讲解第一种(点击上传)、第三种(拖拽上传)方式获取文件夹句柄。(第二种是Web Worker)
点击上传文件夹
- try {
- // 获取文件夹句柄
- const dirHandle = await window.showDirectoryPicker();
- console.log("==> ", dirHandle);
- } catch (error) {}
复制代码
window.showDirectoryPicker() 会返回一个文件夹句柄,里面有几个关键的函数:getDirectoryHandle、getFileHandle、迭代器。
- // 获取文件夹句柄
- const dirHandle = await window.showDirectoryPicker();
- for await (const item of dirHandle.values()) {
- console.log("==> ", item);
- }
复制代码
如果是文件的话,就获取文件句柄,如果是文件夹的话,就获取文件夹句柄,递归直到读取完成:
- function directoryPickerHandle(files: FileSystemDirectoryHandle) {
- const reader = new FileReader();
- // 2. 递归解析文件夹内容
- async function entryFolder(handle: FileSystemDirectoryHandle) {
- for await (const item of handle.values()) {
- // 判断文件还是文件夹
- if (item.kind === "file") {
- // 创建文件读取器, 会返回 FileSystemFileHandle
- const fileHandle = await handle.getFileHandle(item.name);
- // 调用 getFile() 获取文件对象(File)
- const file = await fileHandle.getFile();
- const fullpath =
- (handle.parentName || "") + "/" + handle.name + "/" + file.name;
- console.log("==> 文件完整路径:", fullpath);
- // 创建 Reader 读取器,读取文件内容
- reader.onload = () => {};
- reader.readAsText(file, "UTF-8");
- } else if (item.kind === "directory") {
- // 创建文件夹读取器,返回 FolderSystemHandle
- const folderHandle = await handle.getDirectoryHandle(item.name);
- const fullpath = handle.name + "/" + item.name;
- // 记录该路径
- folderHandle.parentName = handle.name;
- console.log("==> 文件夹完整路径:", fullpath);
- entryFolder(folderHandle);
- }
- }
- }
- // 3. 启动entry
- entryFolder(files);
- }
复制代码
如许就能辨认文件夹内的所有目录、文件内容啦。这里面有一个注意事项哈:const folderHandle = await handle.getDirectoryHandle(item.name); 这里创建的getDirectoryHandle 恰好就是 类型四返回的文件夹句柄,又转向递归,实现读取文件夹操作。
拖拽上传文件夹
DataTransferItem接口的**getAsFileSystemHandle()**方法返回一个FileSystemFileHandle(若拖动的项目是文件),或FileSystemDirectoryHandle(若拖动的项目是目录)。
拖拽上传需要设置HTMLdraggable=“true”,而且监听三个变乱:
- window.onload = () => {
- const box = document.querySelector(".box");
- box?.addEventListener("dragenter", (e) => e.preventDefault());
- box?.addEventListener("dragover", (e) => e.preventDefault());
- box?.addEventListener("drop", (e: Event) => {
- // 阻止导航
- e.preventDefault();
- const event = <DragEvent>e;
- // 获取拖拽的数据
- console.log("==> ", event.dataTransfer);
- });
- };
复制代码
咋一看好像结果为空,明显拖拽文件夹了,结果集照旧空的,那是由于你放手的刹时,数据传输已经完成了。
- box?.addEventListener("drop", (e: Event) => {
- // 阻止导航
- e.preventDefault();
- const event = <DragEvent>e;
- const items = event.dataTransfer?.items;
- if (!items) return;
- for (const item of items) {
- // 获取文件系统句柄
- const entry = item.webkitGetAsEntry()!;
- console.log("==> ", entry);
- }
- });
复制代码
当你拖拽文件和拖拽文件夹时,是差别的数据类型哦,注意一下
拖拽的是文件的话,可以调用 files() 举行文件读取
- const fileEntry = <FileSystemFileEntry>entry;
- fileEntry.file((file: File) => {
- // 创建 fileReader
- const reader = new FileReader();
- reader.readAsText(file, "UTF-8");
- reader.onload = async () => {};
- });
复制代码 拖拽的是文件夹的话,递归继续调用
- // 文件夹,创建读取器
- const folderRntry = <FileSystemDirectoryEntry>entry;
- // 记录需要创建的文件夹路径
- console.log("==> 记录文件夹路径", folderRntry.fullPath);
- const reader = folderRntry.createReader();
- reader.readEntries((files) => {
- files.forEach((file) => dataTransferHandle(file));
- });
复制代码
如果想要实现记录完整文件名称的话,可以跟上诉示例一样,自界说parentName 实现。
总结
我们通过FileSystemFileHandle可以举行文件内容读取、修改文件内容等文件相干操作,同时,使用FileSystemDirectoryHandle提供的本领,可以实现点击上传并辨认文件夹目录,对文件夹内文件举行内容读取,还使用DataTransferItem,实现了拖拽上传文件/文件夹,使用其本领,也能辨认文件夹目录及对文件举行读取操作。同时,拖拽上传还支持纯文本哈,const data = e.dataTransfer.getData(“text”); 这也是一些白板、Canvas-Editor、流程图 从外部拖拽上传实现的原理。
再次声明哈,该API具有肯定兼容性差别,请查阅MDN后,再决定使用生产,同时,请肯定确保使用备用方案,以实现功能的完整性。同时,该文章仅表示本人应用过程实践,大概存在遗漏、错误,烦请指正赐教~
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |