html5 为我们提供了 File api 相关规范。主要涉及 File 接口 和 FileReader 对象 。
本文整理了兼容性检测、文件选择、属性读取、文件读取、进度监控、大文件分片上传以及拖拽上传等开发中常见的前端文件操作。
首先,我们的 File 来自于<input>标签中选中的文件列表。所以,准备如下的 HTML 代码:
<input type="file" id="files" multiple />
<div id="list"></div>
<div id="images"></div>
<!-- File API相关操作写在了script.js中 -->
<script src="./script.js"></script>
File 对象是特殊类型的 Blob。在 script 入口处,应该检测当前浏览器是否支持 File API:
if (!(window.File && window.FileReader && window.FileList && window.Blob)) {
throw new Error("当前浏览器对FileAPI的支持不完善");
}
对于 type 为 file 类型的<input>标签,在选择文件的时候,会触发change事件。用户选中的文件信息也会传入回调函数的第一个参数中。
function handleFileSelect(event) {
const { files } = event.target;
if (!files.length) {
console.log("没有选择文件");
return;
}
console.log("选中的文件信息是:", files);
}
document
.querySelector("#files")
.addEventListener("change", handleFileSelect, false);
event.target.files 是一个FileList对象,它是一个由File对象组成的列表。
每个 File 对象,保存着选中的对应文件的属性。常用的用:
下面,通过 type 属性,过滤掉非图片类型的文件,只展示图片类型文件的信息:
function handleFileSelect(event) {
const { files } = event.target;
if (!files.length) {
console.log("没有选择文件");
return;
}
const innerHTML = [];
const reImage = /image.*/;
for (let file of files) {
if (!reImage.test(file.type)) {
continue;
}
innerHTML.push(
`
<li>
<strong>${file.name}</strong>
(${file.type || "n/a"}) -
${file.size} bytes
</li>
`
);
}
document.querySelector("#list").innerHTML = `<ul>${innerHTML.join("")}</ul>`;
}
还是以图片读取为例,读取并且显示所有的图片类型文件。
文件读取需要使用FileReader对象,它常用 3 个回调方法:
和Image类似,在读取文件之前,需要先绑定事件处理。它读取操作有:readAsArrayBuffer、readAsDataURL、readAsBinaryString、readAsText。传入的参数就是File对象。
那么这几个方法有什么区别呢?不同的读取方式,回调事件onload接受到的event.target.result不相同。比如,readAsDataURL读取的话,result 是一个图片的 url。
下面就是读取图片文件,然后展示的一个例子:
function handleFileSelect(event) {
let { files } = event.target;
if (!files.length) {
return;
}
let vm = document.createDocumentFragment(),
re = /image.*/,
loaded = 0, // 完成加载的图片数量
total = 0; // 总共图片数量
// 统计image文件数量
for (let file of files) {
re.test(file.type) && total++;
}
// onloadstart回调
const handleLoadStart = (ev, file) =>
console.log(`>>> Start load ${file.name}`);
// onload回调
const handleOnload = (ev, file) => {
console.log(`<<< End load ${file.name}`);
const img = document.createElement("img");
img.height = 250;
img.width = 250;
img.src = ev.target.result;
vm.appendChild(img);
// 完成加载后,将其放入dom元素中
if (++loaded === total) {
document.querySelector("#images").appendChild(vm);
}
};
for (let file of files) {
if (!re.test(file.type)) {
continue;
}
const reader = new FileReader();
reader.onloadstart = ev => handleLoadStart(ev, file);
reader.onload = ev => handleOnload(ev, file);
// 读取文件对象
reader.readAsDataURL(file);
}
}
document
.querySelector("#files")
.addEventListener("change", handleFileSelect, false);
在监控读取进度的时候,主要是处理 FileReader 对象上的 onprogress 事件。
下面的例子,请打开一个较大的文件来查看效果(否则一下就读取完了):
function handleFileSelect(event) {
let { files } = event.target;
if (!files.length) {
return;
}
const handleLoadStart = (ev, file) =>
console.log(`>>> Start load ${file.name}`);
const handleProgress = (ev, file) => {
if (!ev.lengthComputable) {
return;
}
// 计算进度,并且以百分比形式展示
const percent = Math.round((ev.loaded / ev.total) * 100);
console.log(`<<< Loding ${file.name}, progress is ${percent}%`);
};
for (let file of files) {
const reader = new FileReader();
reader.onloadstart = ev => handleLoadStart(ev, file);
reader.onprogress = ev => handleProgress(ev, file);
reader.readAsArrayBuffer(file);
}
}
document
.querySelector("#files")
.addEventListener("change", handleFileSelect, false);
在对于超大文件,一般采用分片上传的思路解决。文章开头有讲到,File 是 Blob 的一个特例。而 Blob 上有一个slice 方法,通过它,前端就可以实现分片读取大文件的操作。
为了方便说明,请先准备好一个 txt 文件,文件内容就是:hello world。
示例代码如下,代码中只读取前 5 个字节,由于每个英文字母占 1 个字节,所以打印结果应该是“hello”。
function handleFileSelect(event) {
let { files } = event.target;
if (!files.length) {
return;
}
// 为了方便说明,这里仅仅读取第一个文件
const file = files[0];
// 读取前5个字节的内容
const blob = file.slice(0, 5);
const reader = new FileReader();
// 控制台输出结果应该是:hello
reader.onload = ev => console.log(ev.target.result);
reader.readAsText(blob);
}
document
.querySelector("#files")
.addEventListener("change", handleFileSelect, false);
和前面所述的 File API 相关是完全一样的。唯一需要特殊处理的是文件对象的获取入口改变了。对于<input>标签,监听 onchange 事件,FileList 存放在 event.target.files 中;对于拖拽操作,FileList 存放在拖拽事件的回调函数参数里,通过 event.dataTransfer.files 访问即可。
需要修改一下 html 代码:
<!DOCTYPE html>
<head>
<meta charset="UTF-8">
<style>
#container {
width: 300px;
height: 300px;
border: 3px dotted red;
}
</style>
</head>
<body>
<div id="container"></div>
<script src="./script.js"></script>
</body>
</html>
脚本文件的代码如下:
function handleDropover(event) {
event.stopPropagation();
event.preventDefault();
}
function handleDrop(event) {
event.stopPropagation();
event.preventDefault();
/***** 访问拖拽文件 *****/
const files = event.dataTransfer.files;
console.log(files);
/**********/
}
const target = document.querySelector("#container");
target.addEventListener("dragover", handleDropover);
target.addEventListener("drop", handleDrop);
本文来自《FileAPI 文件操作实战》
其他所有系列都放在了Github
PC端上传文件多半用插件,引入flash都没关系,但是移动端要是还用各种冗余的插件估计得被喷死,项目里面需要做图片上传的功能,既然H5已经有相关的接口且兼容性良好,当然优先考虑用H5来实现。
首先我们定义一个input标签type=file、然后我们定义一个jsReadFiles的方法将文件作为参数;当上传文件的时候读取这个文件。图片上传成功,只是图片路径变成了base64编码的形式。
HTML5读取文件主要利用的就是FileReader这个API,它的使用需要从一个构造函数开始,保存文件的关键是生成文件对象,可以使用URL.createObjectURL()方法实现,该方法能返回给定对象的URL,用在<a>标签的href属性上就可以创建可下载的文件链接。
在JavaScript文件中存储敏感数据,不仅是一种错误的实践方式,而且还是一种非常危险的行为,长期以来大家都知道这一点。
比如我写了一个JS文件,这个文件需要调用另外一个JS文件,该如何实现呢?这篇文章主要介绍:在js文件中引入另一个js文件的实现
最近有这样一个需求,就是在HTML页面中有个按钮导出,点击它,将构造一个文档并存储到本地文件系统中。另外还有个按钮,点击它,从本地文件系统中读取一个文件并对内容进行分析。
从 Yarn 横空出世推出 lock 文件以来,已经两年多时间了,npm 也在 5.0 版本加入了类似的功能,lock 文件越来越被开发者们接收和认可。本篇文章想从前端视角探讨一下我们为什么需要 lock 文件,以及它的一些成本与风险,当然其中一些观点对于后端也是适用的
什么是断点续传?就是下载文件时,不必重头开始下载,而是从指定的位置继续下载,这样的功能就叫做断点续传。前端通过FileList对象获取到相应的文件,按照指定的分割方式将大文件分段,然后一段一段地传给后端,后端再按顺序一段段将文件进行拼接。
form表单的enctype属性:规定了form表单数据在发送到服务器时候的编码方式.。application/x-www-form-urlencoded:默认编码方式,multipart/form-data:指定传输数据为二进制数据,例如图片、mp3、文件,text/plain:纯文本的传输。空格转换为“+”,但不支持特殊字符编码。
适用场景: 网络绝对路径的URL文件或图片,不存储到本地,转换成stream,直接使用HTTPClient传送到SpringBoot的服务端,将文件存储下来,并返回一个文件地址。目前分层架构的系统越来越多这种需求,所以记录下来以备不时之需。
内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!