建设公司网站费用/网络营销推广手段
JavaScript 两种方案打开文件对话框
文章目录
- JavaScript 两种方案打开文件对话框
- 一、文件对话框
- 二、传统方案表单元素🌈
- 三、文件系统访问API💦
- 四、更进一步使用
- 六、代码仓库🌐
- 七、参考资料💘
- 七、推荐博文🍗
一、文件对话框
在编写项目时,难免会遇到想要用户上传文件的场景。文件流处理之前的第一关是打开文件对话框让用户选取文件,本文主要讲解如何打开这个文件对话框,同时带来了一种对于文件系统操作的新概念 API。
要明确一点的是文件对话框是浏览器的功能,开发者不能自定义文件对话框,或是直接操作用户目录文件,要做的只是指引用户打开文件对话框选中目录文件。
二、传统方案表单元素🌈
在网上搜索教程,想要打开文件对话框基本上都是使用 <input type='file' />
方案实现,不用感到奇怪,因为想在大多数浏览器上完全通过 JavaScript 脚本来控制文件对话框,只能通过这个方法。这种传统方案历史最久,最普遍,当然兼容性也是最好的。
通过脚本生成元素 <input type='file' />
并对其操作,就能显示文件对话框,同时生成的元素支持属性不变,利用 accept
和 multiple
属性能控制文件上传类型与多选。
<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>
优缺点总结:
- 不可避免,得依靠生成表单元素才能实现,实现起来比较繁琐。
- 对于更进一步封装,列如使用
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>
除了此方法外,文件系统访问 API 还存在 showSaveFilePicker、showDirectoryPicker 方法等,有兴趣可以去了解一下。
优缺点总结:
- 最直观的好处,使用 showOpenFilePicker 能够摆脱传统
<input type='file' />
方式,简单便捷,极大程度上方便了 Web 应用的开发。 - 受浏览器保护策略影响,文件系统访问 API 程序不能自行运行,需要用户对页面内容交互后才能触发。一般来讲对于文件的操作都是由用户交互所触发,哪怕是传统方案
<input type='file' />
也需用户点击,对此影响并不大。 - 能够自定义兼容浏览器不能识别的 MIME 类型,指定用户能够选择什么文件,功能性更加强大。
- 新的概念所带来的必然是兼容性问题,且文件系统访问 API 不是 W3C 标准,大多数浏览器并不能使用,这是普及起来最大的问题,许多开发者甚至没听说过此 API。
注意:文件系统访问 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-ignoreconst 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 保持一致的编码风格
相关文章:

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

Pycharm远程服务器常见问题
2023年02月23日 问题描述:Pycharm远程服务器跑代码时,不小心把Pycharm关掉了,但服务器代码还在运行? 解决办法:kill进程 先用watch -n 0.5 nvidia_smi查看进程,然后kill -9 <进程> 1、nvidia-smi…...

内容团队如何快速出稿
对于内容团队而言,每个内容选题就相当于一个小项目,它们并非简单的线性工作流,相反其复杂程度不亚于一个小型工厂。一个内容选题会涉及内容形式,选题类型等多个变量,这些变量因素组合起来就是十几种不同类型的工作流。…...

es-08索引的批量操作
索引的批量操作 批量查询和批量增删改 批量查询 GET /_mget#批量查询 GET product/_search GET /_mget {"docs": [{"_index": "product","_id": 2},{"_index": "product","_id": 3}] }GET product/_mge…...

诈金花的概率
游戏使用一副除去大小王的扑克牌,共4个花色52张牌。 1、豹子(AAA最大,222最小)。2、同花顺(AKQ最大,A23最小)。3、同花(AKQ最大,352最小)。4、顺子ÿ…...

ESP32设备驱动-MLX90393磁场传感器驱动
MLX90393磁场传感器驱动 文章目录 MLX90393磁场传感器驱动1、MLX90393介绍2、硬件准备3、软件准备4、驱动实现1、MLX90393介绍 MLX90393 磁场传感器可以在运行时重新编程为不同的模式和不同的设置。 该传感器使用 Melexis 专有的 Triaxis 技术提供与沿 XYZ 轴感应的磁通密度成…...

Java面试题-Spring框架
Spring框架 1. BeanFactory和ApplicationContext有何区别 BeanFactory是Spring最底层的接口,是IoC的核心,定义IoC的基本功能。 BeanFactory具有:延迟实例化的特性。在启动的时候,不会实例化Bean,只有有需要从容器…...

【计算机物理模拟】-力矩、转动惯量和角速度之间的关系
力矩和角速度之间的关系可以通过牛顿第二定律和角动量定理来描述。 牛顿第二定律表明,物体的加速度与作用在物体上的合力成正比,加速度的方向与合力的方向相同。而对于旋转运动的物体,其加速度可以表示为半径 rrr 乘以角加速度 α\alphaα&a…...

async和await用法理解和快速上手 , 同步任务和异步任务顺序安排和轻松理解 , js代码执行顺序表面知道
学习关键语句 : async , await 用法 await 怎么使用 同步任务和异步任务 微任务和宏任务 js中代码执行顺序 写在前面 虽然说 async 和 await 是 Promise 的语法糖 , 但是用惯了Promise 的人(我) , 还真不能超快速使用上这个语法糖 , 所以赶紧写一篇文章出来让各位了解了解这个…...

Linux下java服务占用cpu过高如何处理
Linux下java服务占用cpu过高如何处理 top命令查看进程信息 top按下shiftp,按cpu使用率排行,可见进程1932占用最高,并且是一个java服务 使用jps命令确认java服务 [rootVM-16-16-centos ~]# jps 1011 Jps 9462 yuan_back-0.0.1-SNAPSHOT.jar 1932 spigot-1.18.jar查找异常进程中…...

ros下用kinectv2运行orbslam2
目录 前提 创建工作空间 orbslam2源码配置、测试: 配置usb_cam ROS功能包 配置kinect 前提 vim 、 cmake 、 git 、 gcc 、 g 这些一般都装了 主要是Pangolin 、 OpenCV 、 Eigen的安装 18.04建议Pangolin0.5 创建工作空间 我们在主目录下创建一个catkin_…...

MVP简单模型搭建【架构】
MVP简介 MVP是一种项目架构设计模式(说白了就是我们产品的一种设计方案) 其实MVP本质 就是将View和Model完全隔离,通过Presenter统一调度管理(Presenter扮演着中介的角色)传统的设计思路是我们直接跟房东谈࿰…...

若依ruoyi框架实现目录树与查询页面联动
目录1、业务场景2、前端api.js修改index.vue修改template修改script修改3、后端controllerserviceimpldomainentitytreeselect1、业务场景 后管页面实现目录数与查询页面的联动,类似若依框架用户管理页面。 2、前端 api.js修改 在原有的js文件里配置目录树的查…...

Laravel框架学习笔记——Laravel环境配置及安装(Ubuntu20.04为例)
目录引言1、安装Nginx2、安装PHP3、安装Composer4、搭建Laravel框架项目5、修改Nginx映射6、安装MySQL引言 好久没写博客了,因为个人需要, 所以要涉及到Laravel框架的学习,所以会出一系列的关于PHP的Laravel框架学习笔记,希望能够…...

模拟百度翻译-课后程序(JAVA基础案例教程-黑马程序员编著-第六章-课后作业)
【案例6-5】 模拟百度翻译 【案例介绍】 1.任务描述 大家对百度翻译并不陌生,本案例要求编写一个程序模拟百度翻译。用户输入英文之后搜索程序中对应的中文,如果搜索到对应的中文就输出搜索结果,反之给出提示。本案例要求使用Map集合实现英…...

自然语言处理(NLP)之求近义词和类比词<MXNet中GloVe和FastText的模型使用>
这节主要就是熟悉MXNet框架中的两种模型:GloVe和FastText的模型(词嵌入名称),每个模型下面有很多不同的词向量,这些基本都来自wiki维基百科和twitter推特这些子集预训练得到的。我们只需要导入mxnet.contrib中的text模块即可,这里…...

2023年CDGA考试-第13章-数据质量(含答案)
2023年CDGA考试-第13章-数据质量(含答案) 单选题 1.在导致数据质量问题的常见原因中关于数据输入问题以下描述正确的是: A.数据采集端缺乏数据质量管控 B.相同字段重复设计导致数据不一致 C.缺乏数据采集规范的制定 D.所有描述都正确 答案 D 2.数据质量计划应将其范围限…...

ASEMI高压MOS管ASE65R330参数,ASE65R330图片
编辑-Z ASEMI高压MOS管ASE65R330参数: 型号:ASE65R330 漏极-源极电压(VDS):650V 栅源电压(VGS):20V 漏极电流(ID):12.5A 功耗(P…...

LeetCode动态规划经典题目(九):子序列、子数组问题
目录 31. LeetCode674. 最长连续递增序列 32. LeetCode18. 最长重复子数组 33. LeetCode1143. 最长公共子序列 34. LeetCode1035. 不相交的线 35. LeetCode53. 最大子数组和 36. LeetCode392.判断子序列 37. LeetCode115. 不同的子序列 38. LeetCode583. 两个字符串的删…...

如何利用有限的数据发表更多的SCI论文?——利用ArcGIS探究环境和生态因子对水体、土壤和大气污染物的影响
SCI的写作和发表是科研人提升自身实力和实现自己价值的必要途径。“如何利用有限的数据发表更多的SCI论文?”是我们需要解决的关键问题。软件应用只是过程和手段,理解事件之间的内在逻辑和寻找事物之间的内在规律才是目的。如何利用有限的数据发表更多的…...

六【 SpringMVC框架】
一 SpringMVC框架 目录一 SpringMVC框架1.什么是MVC2.SpringMVC概述3.SpringMVC常见开发方式4.SpringMVC执行流程5.SpringMVC核心组件介绍6.快速构建Spring MVC程序✅作者简介:Java-小白后端开发者 🥭公认外号:球场上的黑曼巴 🍎个…...

【BBuf的CUDA笔记】八,对比学习OneFlow 和 FasterTransformer 的 Softmax Cuda实现
0x1. OneFlow/FasterTransformer SoftMax CUDA Kernel 实现学习 这篇文章主要学习了oneflow的softmax kernel实现以及Faster Transformer softmax kernel的实现,并以个人的角度分别解析了原理和代码实现,最后对性能做一个对比方便大家直观的感受到onefl…...

python 类对象的析构释放代码演示
文章目录一、类的构造函数与析构函数二、代码演示1. 引用的更迭2. 只在函数内部的类对象三、函数内部返回的类对象1. 使用全局变量 引用 函数内部的类对象一、类的构造函数与析构函数 init 函数是python 类的构造函数,在创建一个类对象的时候,就会自动调…...

Hadoop Shell常用命令
Hadoop Shell命令在管理HDFS的时候还是比较常用的,Hadoop Shell命令与shell命令极为相似,但是方便查询,在这里总结分享,大家enjoy~~ 1,cat 语法格式:hadoop fs -cat URI [URI …] 含义:将路径…...

Android中级——色彩处理和图像处理
色彩处理 通过色彩矩阵处理 色彩矩阵介绍 图像的RGBA可拆分为一个4行5列的矩阵和5行1列矩阵相乘 其中4行5列矩阵即为ColorMatrix,可通过调整ColorMatrix间接调整RGBA 第一行 abcde 决定新的 R第二行 fghij 决定新的 G第三行 klmno 决定新的 G第四行 pqrst 决定新…...

C++类和对象:类的定义、类对象的存储、this指针
目录 一. 对于面向过程和面向对象的认识 二. 类 2.1 struct关键字定义类 2.1.1 C语言中的struct关键字 2.1.2 C中的struct关键字 2.2 class关键字 2.1 使用class关键字定义类 三. 类的访问限定及封装 3.1 类的访问权限及访问限定符 3.1.1 访问权限 3.1.2 访问限定…...

代码随想录算法训练营第三十九天 | 62.不同路径,63. 不同路径 II
一、参考资料不同路径https://programmercarl.com/0062.%E4%B8%8D%E5%90%8C%E8%B7%AF%E5%BE%84.html 视频讲解:https://www.bilibili.com/video/BV1ve4y1x7Eu不同路径 IIhttps://programmercarl.com/0063.%E4%B8%8D%E5%90%8C%E8%B7%AF%E5%BE%84II.htmlhttps://progr…...

数据库复习3
一. 简答题(共1题,100分) 1. (简答题) 存在数据库test,数据库中有如下表: 1.学生表 Student(Sno,Sname,Sage,Ssex) --Sno 学号,Sname 学生姓名,Sage 出生年月,Ssex 学生性别 主键Sno 2.教师表 Teacher(Tno,Tname) --T…...

顺序表的增删查改
数据结构 是数据存储的方式,对于不同的数据我们要采用不同的数据结构。就像交通运输,选用什么交通工具取决于你要运输的是人还是货物,以及它们的数量。 顺序存储结构 包括顺序表、链表、栈和队列等。 例如腾讯QQ中的好友列表,…...

jupyter matplotlib中文乱码解决
中文乱码可能有两种情况 1. matplotlib里面有中文字体 2. 没有中文字体 查看是否有中文字体: # 查询当前系统所有字体 from matplotlib.font_manager import FontManager import subprocessmpl_fonts = set(f.name for f in FontManager().ttflist)print(all font list get f…...