读取文件内容、修改文件内容、识别文件夹目录(Web操作系统文件/文件夹详解)

news2024/11/14 8:54:25

前言

        因 Unicode IDE 编辑器导入文件、文件夹需要,研究了下导入文件/文件夹的功能实现,发现目前相关文章有点少,故而记录下过程,如果有误,还望指正。(API的兼容性及相关属性、接口定义,请自行查看文件系统 API)

 文件 API

        文件 API 使得 web 应用可以访问文件和其中的内容,当我们想要访问一个文件,可以通过<input type="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、Base64icon-default.png?t=O83Ahttps://juejin.cn/post/7148254347401363463?searchId=20241113152541CCE5E6FEB74927A4CFEBjs二进制及其相关转换全总结(File、Blob、FileReader、ArrayBuffer、Base64、Object URL、DataURL...)icon-default.png?t=O83Ahttps://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(若拖动的项目是目录)。

拖拽上传需要设置HTML  draggable="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后,再决定使用生产,同时,请一定确保使用备用方案,以实现功能的完整性。同时,该文章仅表示本人应用过程实践,可能存在遗漏、错误,烦请指正赐教~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2240056.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

「Mac玩转仓颉内测版2」入门篇2 - 编写第一个Cangjie程序

本篇详细介绍在Mac系统上创建首个Cangjie项目并编写、运行第一个Cangjie程序的全过程。内容涵盖项目创建、代码编写、程序运行与调试&#xff0c;以及代码修改后的重新运行。通过本篇&#xff0c;掌握Cangjie项目的基本操作&#xff0c;进一步巩固开发环境的配置&#xff0c;迈…

微信小程序_小程序视图与逻辑_day3

一、目标 A. 能够知道如何实现页面之间的导航跳转 B. 能够知道如何实现下拉刷新效果 C. 能够知道如何实现上拉加载更多效果 D. 能够知道小程序中常用的生命周期 二、目录 A. 页面导航 B. 页面事件 C. 生命周期 D. WXS脚本 E. 案例-本地生活&#xff08;列表页面&#xff09;…

Threejs 材质贴图、光照和投影详解

1. 材质和贴图 材质&#xff08;Material&#xff09;定义了物体表面的外观&#xff0c;包括颜色、光泽度、透明度等。贴图&#xff08;Textures&#xff09;是应用于材质的图像&#xff0c;它们可以增加物体表面的细节和真实感。 1.1材质类型 MeshBasicMaterial&#xff1a…

论文概览 |《Sustainable Cities and Society》2024.11 Vol.115(下)

本次给大家整理的是《Sustainable Cities and Society》杂志2024年11月第115期的论文的题目和摘要&#xff0c;一共包括76篇SCI论文&#xff01;由于论文过多&#xff0c;我们将通过两篇文章进行介绍&#xff0c;本篇文章介绍第31--第76篇论文! 论文31 The effect of urban fo…

【问卷调研】HarmonyOS SDK开发者社区用户需求有奖调研

问卷请点击&#xff1a;HarmonyOS SDK开发者社区用户需求有奖调研

【3D Slicer】的小白入门使用指南五

心脏CT多组织半自动分割(解说版) 模块勾选的是converters下的crop volume 右侧移动滑块+移动边框,移动位置找到自己关注的区域即可,然后点击apply(其他参数也要设置的和图片一致) 模块勾选segment editor,并创建心脏多个不同分割位置(默认下不同位置自动分配了不同的颜…

丹摩征文活动|FLUX.1 和 ComfyUI:从部署到上手,轻松驾驭!

FLUX.1 和 ComfyUI&#xff1a;从部署到上手&#xff0c;轻松驾驭&#xff01; FLUX.1历史曲线 黑森林实验室推出了一款名为FLUX.1的先进图像生成模型&#xff0c;根据不同用户需求&#xff0c;提供了三种独特的版本。 FLUX.1-pro&#xff1a;作为专为企业打造的强大闭源版本…

自动驾驶合集(更新中)

文章目录 车辆模型控制路径规划 车辆模型 车辆模型基础合集 控制 控制合集 路径规划 规划合集

string------1

文章目录 一. STL1.概念2.版本 二. string类2.1 为什么学习string类2. 标准库中的string类2.2.1 构造&#xff08;7个&#xff09;2.2.2 对string类对象进行“访问和修改”&#xff08;1&#xff09;operator[]&#xff08;2&#xff09;迭代器1.迭代器的使用2.迭代器的价值&am…

开源 2 + 1 链动模式、AI 智能名片、S2B2C 商城小程序在用户留存与品牌发展中的应用研究

摘要&#xff1a;本文以企业和个人品牌发展中至关重要的用户留存问题为切入点&#xff0c;结合管理大师彼得德鲁克对于企业兴旺发达的观点&#xff0c;阐述了用户留存对品牌营收的关键意义。在此基础上&#xff0c;深入分析开源 2 1 链动模式、AI 智能名片、S2B2C 商城小程序在…

蓝桥杯每日真题 - 第8天

题目&#xff1a;&#xff08;子2023&#xff09; 题目描述&#xff08;14届 C&C B组A题&#xff09; 解题思路&#xff1a; 该代码通过动态计算包含数字 "2023" 的子序列出现次数。主要思路是&#xff1a; 拼接序列&#xff1a;将1到2023的所有数字按顺序拆分…

Mac Nginx 前端打包部署

安装homebrew /bin/zsh -c "$(curl -fsSL https://gitee.com/cunkai/HomebrewCN/raw/master/Homebrew.sh)" 安装Nginx brew install nginx nginx相关命令 nginx启动命令&#xff1a;nginx nginx -s reload #重新加载配置 nginx -s reopen #重启 nginx -s stop #…

小试银河麒麟系统OCR软件

0 前言 今天在国产电脑上办公&#xff0c;需要从一些PDF文件中复制文字内容&#xff0c;但是这些PDF文件是图片转换生成的&#xff0c;不支持文字选择和复制&#xff0c;除了手工输入&#xff0c;我们还可以使用OCR。 1 什么是OCR OCR &#xff08;Optical Character Recogni…

选择主动孤独,亦可以成长和放松

有的人热衷于喧嚣&#xff0c;享受来自社交场合的灯火辉煌&#xff0c;将欢声笑语作为心灵的慰藉。有的人则偏爱那份宁静&#xff0c;更愿意在青灯古巷间徘徊&#xff0c;在山川湖海间独行&#xff0c;以一杯茶、一卷书、一段旅程与自我对话。每个人以不同的方式诠释活着的意义…

Django 外键引用另一个表中的多个字段

在 Django 中&#xff0c;外键&#xff08;ForeignKey&#xff09;通常只引用另一张表的一个字段&#xff0c;比如一个主键或一个唯一标识字段。然而&#xff0c;如果我们需要让一个外键引用另一张表中的多个字段&#xff0c;通常有以下几种方法来实现这种关系。 1、问题背景 …

MyBatis从入门到进阶

目录 MyBatis入门1、创建项目、数据准备2、数据库配置3、编写持久层代码单元测试打印日志 基本操作查询数据插入数据删除数据更新数据 MyBatis - xml插入数据更新数据删除数据查询数据#{}与${}SQL注入排序like查询 MyBatis进阶if标签trim标签where标签set标签foreach标签sql标签…

【JavaWeb】JavaWeb入门之XML详解

目录 1.XML介绍 1.1.XML概述 1.1.1.什么是XML 1.1.2.XML的作用 1.1.3.XML与HTML的比较 1.1.4.XML和properties&#xff08;属性文件&#xff09;比较 1.1.5.W3C组织 1.2.XML语法概述 1.2.1.XML文档展示 1.2.2.XML文档的组成部分 1.3.XML文档声明 1.3.1.什么是XML文…

wordcloud库基本介绍

文章目录 wordcloud库概述wordcloud库的安装 wordcloud库使用说明配置对象参数 wordcloud应用实例实例: 政府工作报告词云 wordcloud库概述 wordcloud是优秀的词云展示第三方库 词云以词语为基本单位,更加直观和艺术地展示文本 wordcloud库的安装 (cmd命令行) pip install …

VMware和CentOS 7.6 Linux操作系统的安装使用

1. 安装VMware 安装VMware之前&#xff0c;有些电脑是需要去BIOS里修改设置开启cpu虚拟化设备支持才能安装。如果运气不好在安装过程中安装不了的话就自行百度吧。 打开 VMware 的官网: https://www.vmware.com/ 点击 product&#xff0c;往下滑找到 see desktop hypeerviso…

LLM在Transformer上的改动

LLM在Transformer上的改动 1.multi-head共享1.1BERT的逻辑1.2multi-head共享 2.attention的前后网络2.1传统Transformer&#xff1a;2.2GPTJ结构&#xff1a; 3.归一化层的位置&#xff08;LayerNorm&#xff09;4.归一化层函数的选择4.1LayerNorm4.2RMSNorm 3.激活函数4.LLama…