JavaScript 两种方案打开文件对话框

news2025/7/26 19:25:48

JavaScript 两种方案打开文件对话框.png

JavaScript 两种方案打开文件对话框

文章目录

  • JavaScript 两种方案打开文件对话框
    • 一、文件对话框
    • 二、传统方案表单元素🌈
    • 三、文件系统访问API💦
    • 四、更进一步使用
    • 六、代码仓库🌐
    • 七、参考资料💘
    • 七、推荐博文🍗


一、文件对话框

在编写项目时,难免会遇到想要用户上传文件的场景。文件流处理之前的第一关是打开文件对话框让用户选取文件,本文主要讲解如何打开这个文件对话框,同时带来了一种对于文件系统操作的新概念 API。
要明确一点的是文件对话框是浏览器的功能,开发者不能自定义文件对话框,或是直接操作用户目录文件,要做的只是指引用户打开文件对话框选中目录文件。


二、传统方案表单元素🌈

在网上搜索教程,想要打开文件对话框基本上都是使用 <input type='file' /> 方案实现,不用感到奇怪,因为想在大多数浏览器上完全通过 JavaScript 脚本来控制文件对话框,只能通过这个方法。这种传统方案历史最久,最普遍,当然兼容性也是最好的。
通过脚本生成元素 <input type='file' /> 并对其操作,就能显示文件对话框,同时生成的元素支持属性不变,利用 acceptmultiple 属性能控制文件上传类型与多选。

<style>
  .wrap {
    display: flex;
    height: 20vh;
  }

  #open-file {
    width: 100px;
    height: 50px;
    margin: auto;
    border: 1px solid #5B5B5B;
    border-radius: 5px;
    background-color: #FCFCFC;
    cursor: pointer;
  }

  #open-file:hover {
    background-color: #F0F0F0;
  }
</style>
<body>
  <div class="wrap">
    <button id="open-file">选择文件</button>
  </div>
  <script>
    /**
   * 打开文件选取对话框
   * @param fn 选取文件后回调,接收event和filelist参数
   * @param accept 文件类型
   * @param multiple 是否多选
   */
    function openFilePicker({fn, accept, multiple} = {}) {
      const inpEle = document.createElement("input");
      inpEle.id = `__file_${Math.trunc(Math.random() * 100000)}`;
      inpEle.type = "file";
      inpEle.style.display = "none";
      // 文件类型限制
      accept && (inpEle.accept = accept);
      // 多选限制
      multiple && (inpEle.multiple = multiple);
      inpEle.addEventListener("change", event => fn.call(inpEle, event, inpEle.files), {once: true});
      inpEle.click();
    }

    const btn = document.getElementById("open-file");
    btn.addEventListener("click", () => {
      openFilePicker({
        fn: (e, files) => {
          console.group("获取到的文件");
          console.log("files", files);
          console.groupEnd();
        }
      });
    });
  </script>
</body>

图 2-1 .gif
优缺点总结:

  • 不可避免,得依靠生成表单元素才能实现,实现起来比较繁琐。
  • 对于更进一步封装,列如使用 Promise 在用户取消选择文件抛出 reject 变的难以检测,如何监听取消行为在 stackoverflow 上貌似也没有什么好的解决办法,不过也不用慌张,大多数情况下只需处理文件获取后的行为。

三、文件系统访问API💦

文件系统访问API是一个很新的概念,允许web应用程序直接读取或保存用户设备上的文件和文件夹的更改,此 API 目前纯粹是一个 JavaScript API,并且不与表单或输入元素集成,这和以往的<input type='file' />不同。
window.showOpenFilePicker 方法能够直接调用文件对话框,一般配合aysnc/await使用,获取到的是一个文件句柄对象数组。
参数:

  • multiple:默认为false,当设置为true是,可以选择多个文件。
  • excludeAcceptAllOption:在打开文件对话框时,右下角分类选项通常存在一个「所有文件」选项,此属性控制该选项是否存在。默认为false,则显示「所有文件」选项。
  • types:允许用户选择的文件类型数组,数组中的每个元素都是一个对象,对应文件对话框右下角分类的一个项。
    • description:可选,表示文件或则文件夹的描述,对应文件对话框分类选项说明,若为指定则浏览器自动生成。
    • accept:接受的文件类型,传入对象,键是MIME类型,值是一个文件扩展名数组。浏览器能识别 MIME 类型的情况下,会将扩展名数组与内置扩展名进行合并。
      「在传统方案 <input type='file' />里也存在一个类型属性 accept,有意思的是只需要填入MIME类型即可,浏览器会自动识别对应的后缀文件,部分未认证或偏僻的MIME类型浏览器是识别不了,猜测是为了解决这个问题将此参数以这种形式编写。需要了解更多MIME类型,可以去查阅这篇文章『 『速查手册』MIME 多用途互联网邮件扩展 』」

下述示例限制只能传图片类型文件,但事实上能支持的类型远远不止所设置的几个。注意:因为兼容性问题,运行下述代码请使用谷歌内核86+版本浏览器。

<style>
  .wrap {
    display: flex;
    height: 20vh;
  }

  #open-file {
    width: 100px;
    height: 50px;
    margin: auto;
    border: 1px solid #5B5B5B;
    border-radius: 5px;
    background-color: #FCFCFC;
    cursor: pointer;
  }

  #open-file:hover {
    background-color: #F0F0F0;
  }
</style>
<body>
  <div class="wrap">
    <button id="open-file">选择文件</button>
  </div>
  <script>
    const btn = document.getElementById("open-file");
    btn.addEventListener("click", async () => {
      // 单元素数组结构
      const [fileHandle] = await window?.showOpenFilePicker({
        types: [
          {
            description: "图片类型",
            accept: {"image/*": ['.png', '.gif', '.jpeg', '.jpg']}
          }
        ]
      });
      // 获取文件File对象
      const file = await fileHandle?.getFile();
      console.group("获取到的文件");
      console.log(fileHandle);
      console.log(file);
      console.groupEnd();
    });
  </script>
</body>

4.1 轮播动画.gif
image.png

除了此方法外,文件系统访问 API 还存在 showSaveFilePicker、showDirectoryPicker 方法等,有兴趣可以去了解一下。

优缺点总结:

  • 最直观的好处,使用 showOpenFilePicker 能够摆脱传统 <input type='file' />方式,简单便捷,极大程度上方便了 Web 应用的开发。
  • 受浏览器保护策略影响,文件系统访问 API 程序不能自行运行,需要用户对页面内容交互后才能触发。一般来讲对于文件的操作都是由用户交互所触发,哪怕是传统方案 <input type='file' /> 也需用户点击,对此影响并不大。
  • 能够自定义兼容浏览器不能识别的 MIME 类型,指定用户能够选择什么文件,功能性更加强大。
  • 新的概念所带来的必然是兼容性问题,且文件系统访问 API 不是 W3C 标准,大多数浏览器并不能使用,这是普及起来最大的问题,许多开发者甚至没听说过此 API。

image.png
image.png
image.png

注意:文件系统访问 API 它不是 W3C 标准,也不在 W3C 标准轨道上,在can i use上查询可知,在谷歌内核以及少部分的浏览器上支持此 API ,且版本要求苛刻。可以简单通俗的理解为,这可能只是谷歌的工程师为谷歌浏览器专门开发的(开发文档日志),在火狐浏览器控制台上,调用 window.showOpenFilePicker API 时,返回的是未定义。


四、更进一步使用

有句话说的好,小孩才做选择,大人两个都要,那么有没有什么办法可以实现传统方式与文件系统访问API相结合呢?答案是肯定的。
实现原理很简单,只需根据 window 对象下判断是否存在 showOpenFilePicker 方法即可。下述代码引用博主 开源项目 的源码供大家参考。因为是使用 TypeScript 编写,需要转换为 JavaScript 的同学需要使用 Node.js 安装 typescript依赖。

npm i typescript
tsc 文件目录 --target esnext 
/**
 * 打开文件选择对话框
 * 若浏览器不支持实验性方法:window.showOpenFilePicker,则采用input[type=file]元素进行兼容
 * @param {string | string[]} accept 文件类型限制 ,默认全部
 * @param {boolean} multiple 文件多选
 * @param {boolean} webkitdirectory 只选择目录限制
 * @param {number} compatible 兼容模式,默认开启
 * @param {number} cancel 兼容取消控制,为0时候则取消文件时不抛出reject,❗在使用async/await时会造成阻塞
 * @param {string} description 文件或者文件夹的描述,可选
 * @return {Promise<FileList>}
 */
export async function openFileDialog(
  {
    accept = MIME.ALL,
    compatible = true,
    cancel = 300,
    multiple,
    webkitdirectory,
    description
  }: FileDialogConfig = {}
): Promise<File[]> {
  accept.constructor === Array && (accept = accept.join(","));
  // 实验性功能
  if (!compatible && window.hasOwnProperty("showOpenFilePicker")) {
    console.warn("Note that showOpenFilePicker is an experimental interface and is not supported by most browsers, so use it sparingly.");
    const files = [];
    const acceptMap: { [accept: string]: string[] } = {};
    for (let a of (accept as string).split(",")) {
      acceptMap[a] = [];
    }
    //@ts-ignore
    const fileHandleList = await window.showOpenFilePicker?.({
      multiple,
      excludeAcceptAllOption: false,
      types: [{
        description,
        accept: acceptMap
      }]
    });
    for (const f of fileHandleList) {
      files.push(await f.getFile());
    }
    return files;
  }

  const inpEle = document.createElement("input");
  inpEle.id = `__file_${Math.trunc(Math.random() * 100000)}`;
  inpEle.type = "file";
  inpEle.style.display = "none";
  // 文件类型限制
  inpEle.accept = accept as string;
  // 多选限制
  multiple && (inpEle.multiple = multiple);
  // 选择目录限制
  if (webkitdirectory) {
    console.warn("该特性是非标准的,请尽量不要在生产环境中使用它!\n"
                 + "This feature is non-standard, so try not to use it in a production environment!");
    inpEle.webkitdirectory = webkitdirectory;
  }
  inpEle.click();

  return await new Promise((resolve, reject) => {
    let _isSelected = false;
    const changeEvent = () => {
      const files = inpEle.files;
      if (files) {
        _isSelected = true;
        resolve(Array.from(files));
      }
    };
    const focusEvent = (event: Event) => {
      if (event.target?.constructor === Window) {
        setTimeout(() => {
          !_isSelected && reject("未选定文件\nUnselected file");
        }, cancel);
      }
    };
    inpEle.addEventListener("change", changeEvent, {once: true});
    cancel && window.addEventListener("focus", focusEvent, {once: true});
  });
}

六、代码仓库🌐

  • https://github.com/xzcwx/files

七、参考资料💘

🍅因发布平台差异导致阅读体验不同,源文贴出:《JavaScript 两种方案打开文件对话框》

  • 官方手册:
    • - HTML(超文本标记语言) | MDN
    • Window.showOpenFilePicker() - Web APIs | MDN
    • 文件系统 API 的基本概念 - Web API 接口参考 | MDN
    • can i use
  • 网络文献:
    • The File System Access API: simplifying access to local files
    • W3C社区文档-文件系统访问(英)

七、推荐博文🍗

  • 『速查手册』MIME 多用途互联网邮件扩展
  • 『精』EditorConfig 小老鼠 跨编辑器 | IDE 保持一致的编码风格

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

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

相关文章

ZYNQ双核处理器独立运行AMP

一、简介多核处理器从多核的结构上是否一致&#xff0c;分为两种基本架构&#xff1a;同构多核架构和异构多核架构。同构多核处理器是指系统中的处理器在结构上是相同的&#xff1b;而异构处理器是指系统中的处理器在结构上是不同的&#xff0c;这些处理器可以是通用处理器&…

MySQL递归查询 三种实现方式

1 建表脚本1.1 建表DROP TABLE IF EXISTS sys_region; CREATE TABLE sys_region (id int(50) NOT NULL AUTO_INCREMENT COMMENT 地区主键编号,name varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 地区名称,short_name varchar(50) CHARA…

WebDAV之π-Disk派盘+Cloud Player

Cloud Player 支持WebDAV方式连接π-Disk派盘。 推荐一款云媒体播放器是存储在常见云平台中的内容的通用播放器。 Cloud Player云媒体播放器是存储在常见云平台中的内容的通用播放器,无需将其下载到设备。支持以下云平台:Google Drive、DropBox、One Drive、WebDav等。此外,…

BI解析,告诉你企业为什么选择商业智能BI?

数据的价值目前在商业世界和现代化城市建设中尤为突出&#xff0c;先说商业世界&#xff0c;数据在这里成为了各领域企业的重要资产&#xff0c;不仅让众多企业重新构建了以数据为企业文化、核心驱动力的数据体系&#xff0c;还把企业的财务、销售、运营、市场、研发等不同部门…

写出高质量的前端代码之消除代码中的重复

软件开发中&#xff0c;有个很重要的DRY原则&#xff0c;即Dont Repeat Yourself&#xff0c;也就是不要重复自己。 重复的代码会带来以下问题&#xff1a; 开发效率低&#xff0c;重复造轮子不同人开发的同一类功能&#xff0c;质量层次不齐修改问题时可能会遗漏&#xff0c…

vue3 + vite + ts 集成mars3d

vue3 vite ts 集成mars3d 文章目录vue3 vite ts 集成mars3d前言一、创建一个vue3 vite ts项目二、引入mars3d相关依赖三、vite.config.ts 相关配置四、 新建DIV容器 创建地图前言 使用mars3d过程中&#xff0c;需要集成mars3d到自己的项目中&#xff0c;mars3d开发教程…

MFC实现曲柄滑块以及铰链四杆机构的运动仿真

创建MFC应用&#xff0c;选择单文档&#xff0c;在应用程序类型中的应用程序类型选择单文档&#xff0c;在项目样式中选择MFC standard&#xff0c;在用户界面功能中的命令行中选择 使用菜单栏和工具栏。这样选择界面更好看一点&#xff0c;下面给出截图&#xff1a;在资源视图…

代码随想录算法训练营第三十八天 | 理论基础 ,509. 斐波那契数,70. 爬楼梯,746. 使用最小花费爬楼梯

Day36 周日休息~一、参考资料理论基础https://programmercarl.com/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E7%90%86%E8%AE%BA%E5%9F%BA%E7%A1%80.html 刷题大纲递推五部曲&#xff1a;确定dp数组&#xff08;dp table&#xff09;以及下标的含义确定递推公式dp数组如何初始化确…

云计算专业和计算机专业哪个好就业?

云计算专业其实也是属于计算机类专业呢&#xff0c;他包括了计算机硬件设备、计算机网络、磁盘柜、操作系统、中间件、数据库、服务器/虚拟机、应用软件开发等技术内容&#xff0c;云计算技术是以IT服务的形式面向用户的&#xff1b;所以云计算不是一门技术&#xff0c;而是众多…

一个底层30岁的测试员的故事,连躺平都是奢望的....

背景 我是一个生活在某二线城市的测试员&#xff0c;家里面有两位小朋友&#xff0c;老大现在读幼小衔接&#xff0c;明年上小学&#xff0c;老二两岁多一点点&#xff0c;明年上幼儿园。家庭主要开支有房贷、车贷、车位贷、保险、时不时小意外、还有一笔 3万的信用卡分期&…

【亲测】PHP进销存源码 ERP多仓库管理系统 网络版手机端+小程序版进销存 二次开发

可电脑端操作&#xff0c;也可以小程序端操作&#xff0c;非常方便&#xff01;适合小型企业个人使用。 服务端thinkphp5全套开源源码&#xff0c;uniapp前端全套开源 功能 1、支持采购单录入、审核、入库、退货等采购过程中的记录追踪 2、支持销售、出库、销售审核、出库审核…

Smtplib之发邮件模块

目录 创建Smtp对象 Smtp类中的方法 MIME MIMEBase MIMEBase MIMEMultipart MIMEApplication MIMEAudio MIMEImage MIMEText 实例 texthtml格式 发送带图片附件的邮件 发送带附件的邮件 含多种格式 SMTP模块 SMTP 简单传输协议&#xff0c;它是一组用于由源…

JAVA商城源码-多用户商城系统源码-B2B2C商城系统

项目介绍 三勾多商户小程序商城基于springbootelement-uiuniapp打造的面向开发的小程序商城&#xff0c;方便二次开发或直接使用&#xff0c;可发布到多端&#xff0c;包括微信小程序、微信公众号、QQ小程序、支付宝小程序、字节跳动小程序、百度小程序、android端、ios端。 采…

leaflet 导出图片,打印图片(A4横版或竖版)

第093个 点击查看专栏目录 本示例的目的是介绍如何在vue+leaflet中打印图片导出图片。一个简单的leaflet插件示例,添加了一个图标来打印或导出地图。 直接复制下面的 vue+openlayers源代码,操作2分钟即可运行实现效果. 文章目录 示例效果配置方式示例源代码(共85行)安装插…

自然语言处理(NLP)之word2vec的实现(PTB语料库)<找语义相近的词>

在2013年Google开源了一款用于词向量计算的工具&#xff1a;word2vec&#xff0c;它本身不是一种深度学习之类的模型&#xff0c;是一种用于计算词嵌入的体系结构。实际上大家平时说的这个指代的就是前面介绍过的跳字(元)模型与连续词袋模型CBow&#xff1a;自然语言处理(NLP)之…

OpenCV-Python学习(21)—— OpenCV 图像几何变换之图像翻转(cv.flip、np.flip)

1. 学习目标 学习 OpenCV 图像的翻转函数 cv.flip&#xff1b;学习 NumPy 矩阵的反转函数 np.flip&#xff1b;自己实现矩阵反转的函数。 2. OpenCV 翻转 翻转也称镜像&#xff0c;是指将图像沿轴线进行轴对称变换。水平镜像是将图像沿垂直中轴线进行左右翻转&#xff0c;垂直…

写出高质量的前端代码之降低耦合提升正交性

耦合与正交性 什么是耦合 在百度百科中&#xff0c;对耦合的解释 耦合是指两个或两个以上的体系或两种运动形式间通过相互作用而彼此影响以至联合起来的现象。 我曾经买过一个遥控飞机玩具&#xff0c;当我推前进杆的时候&#xff0c;飞机除了前进&#xff0c;还会往左或者往…

字符串匹配--strstr函数的模拟实现思路和代码

一&#xff0c;strstr函数 原型&#xff1a; const char * strstr ( const char * str1, const char * str2 );char * strstr ( char * str1, const char * str2 ); strstr是一个字符串匹配函数&#xff0c;在str1中去寻找str2&#xff0c;如果找到&#xff0c;返回str2在…

科研快讯 | 14篇论文被信号处理领域顶级国际会议ICASSP录用

ICASSP 2023 近日&#xff0c;2023年IEEE声学、语音与信号处理国际会议&#xff08;2023 IEEE International Conference on Acoustics, Speech, and Signal Processing&#xff0c;ICASSP 2023&#xff09;发布录用通知&#xff0c;清华大学人机语音交互实验室&#xff08;TH…

【LSTM】2 多因素单步骤预测

基于时间序列的预测&#xff0c;一定要明白它的原理&#xff0c;不是工作原理&#xff0c;而是工程落地原因。 基于时间序列&#xff0c;以已知回归未知----这两句话是分量很重的。 多因素单步单输出组合 时间序列&#xff1a;t1 是 特征 1,2,3 预测t2 的回归值41 多因素单步多…