说明
Web 应用程序的一个主要的痛点是无法操作用户计算机上的文件。
2000 年之前,处理文件的唯一方式是把放到一个表单里,仅此而已。
File API 与 Blob API 是为了让 Web 开发者能以安全的方式访问客户端机器上的文件,从而更好地与这些文件交互而设计的。
文件API关系图谱
==================
Blob 类型
Blob 继承自Object对象,表示二进制大对象(binary larget object),是 JavaScript 对不可修改二进制数据的封装类型。
包含字符串的数组、ArrayBuffers、ArrayBufferViews,甚至其他 Blob 都可以用来创建 blob。
创建
Blob构造函数可以接收一个 options 参数,并在其中指定的媒体类型 MIME:
console.log(new Blob(['foo']));// Blob {size: 3, type: ""}console.log(new Blob(['{"a": "b"}'], { type: 'application/json' }));// {size: 10, type: "application/json"}console.log(new Blob(['<p>Foo</p>', '<p>Bar</p>'], { type: 'text/html' }));// {size: 20, type: "text/html"}
Blob 对象有一个 size 属性和一个 type 属性
方法总览

选择部分数据(资源分段)
只读取部分文件可以节省时间,特别是在只需要数据特定部分比如文件头的时候
let newblob = new Blob(['{"a": "b"}'],{ type: 'application/json' })// 格式为 slice(开始位置,结束位置,类型),都是可选的参数let sb = newblob.slice(0,4,{ type: 'application/json'}) // 只获取1-4字节的数据,返回的也是一个blob对象
File 类型
MDN文档:https://developer.mozilla.org/zh-CN/docs/Web/API/File
每个 File 对象都有一些只读属性:
1、name:本地系统中的文件名,只读。
2、size:以字节计的文件大小,只读。
3、type:包含文件 MIME 类型的字符串。
4、lastModifiedDate:表示文件最后修改时间的字符串,不建议使用。
4、lastModified:返回当前 File 对象所引用文件最后修改时间,自 UNIX 时间起始值(1970年1月1日 00:00:00 UTC)以来的毫秒数。
FileReader 类型
FileReader类型表示一种异步文件读取机制。
可以把FileReader 想象成类似于XMLHttpRequest,只不过是用于从文件系统读取文件,而不是从服务器读取数据。
方法
FileReader 类型提供了几个读取文件数据的方法:
1、readAsText(file, encoding):从文件中读取纯文本内容并保存在 result 属性中。第二个参数表示编码,是可选的。
2、readAsDataURL(file):读取文件并将内容的数据 URI 保存在 result 属性中,Base64格式。(更适合展示图片)
3、readAsArrayBuffer(file):读取文件并将文件内容以 ArrayBuffer 形式保存在 result 属性。
钩子函数(过程方法)
因为这些读取方法是异步的,所以每个 FileReader 会发布几个事件,其中 3 个最有用的事件是onprogress、onerror 和 onload,分别表示还有更多数据、发生了错误和读取完成。
onprogress 事件每 50 毫秒就会触发一次,其与 XHR 的 progress 事件具有相同的信息:lengthComputable、loaded 和 total。
此外,在 progress 事件中可以读取 FileReader 的 result属性,即使其中尚未包含全部数据。
onerror 事件会在由于某种原因无法读取文件时触发。触发 error 事件时,FileReader 的 error属性会包含错误信息。这个属性是一个对象,只包含一个属性:code。这个错误码的值可能是 1(未找到文件)、2(安全错误)、3(读取被中断)、4(文件不可读)或 5(编码错误)。
onabort 如果想提前结束文件读取,则可以在过程中调用 abort()方法,从而触发 abort 事件。
onload 事件会在文件成功加载后触发。
onloadend load、error 和 abort 事件触发后,还会触发 loadend 事件,表示整个过程完全结束
例子
见下应用的展示
FileReaderSync 类型
FileReaderSync 类型就是 FileReader 的同步版本。
这个类型拥有与 FileReader相同的方法,只有在整个文件都加载到内存之后才会继续执行。
FileReaderSync 只在工作线程中可用,因为如果读取整个文件耗时太长则会影响全局。
假设通过 postMessage()向工作线程发送了一个 File 对象。以下代码会让工作线程同步将文件读取到内存中,然后将文件的数据 URL 发回来
// worker.jsself.omessage = (messageEvent) => {const syncReader = new FileReaderSync();console.log(syncReader); // FileReaderSync {}// 读取文件时阻塞工作线程const result = syncReader.readAsDataUrl(messageEvent.data);// PDF 文件的示例响应console.log(result); // data:application/pdf;base64,JVBERi0xLjQK...// 把 URL 发回去self.postMessage(result);};
URL 类型
对象 URL 有时候也称作 Blob URL,是指引用存储在 File 或 Blob 中数据的 URL。
对象 URL 的优点是不用把文件内容读取到 JavaScript 也可以使用文件。
==============
应用
1、选择本地文件
选择文件
// <input type="file" id="fileInput">// <input type="file" multiple id="fileInput"> 表示可以多选// <input type="file" multiple accept="image/*" id="fileInput"> 限定文件类型let fileInput = document.getElementById("fileInput");// 给input添加 change 改变事件,也就是用户选择的文件发生变化时触发fileInput.addEventListener("change", (event) => {console.log(event.target.files) // 获取选择后的fileList对象,里面就是file类型的对象,是个数组// 然后可以处理文件 event.target.files});

替换丑陋的原生
(这是丑陋的原生input)
// 1、通过style="display:none"隐藏input标签// <input type="file" id="fileInput" multiple accept="image/*" style="display:none" onchange="handleFiles(this.files)">// 2、设置你想要的按钮// <button id="myBtn">点我选择文件</button>// 3、获取input 和 按钮const myBtn = document.getElementById("myBtn");const fileInput = document.getElementById("fileInput");// 4、给按钮添加点击事件,然后触发input的click() 方法myBtn.addEventListener("click", function (e) {if (fileInput) {fileInput.click();}}, false);// 5、给input添加 change 改变事件,也就是用户选择的文件发生变化时触发fileInput.addEventListener("change", (event) => {console.log(event.target.files) // 获取选择后的fileList对象,里面就是file类型的对象,是个数组// 然后可以处理文件 event.target.files});
拖放选择文件
在页面上创建放置目标后,可以从桌面上把文件拖动并放到放置目标。
这样会像拖放图片或链接一样触发 drop 事件。
被放置的文件可以通过事件的 event.dataTransfer.files 属性读到,这个属性保存着一组 File 对象,就像文本输入字段一样。
// 1、获取把文件拖放到的元素let droptarget = document.getElementById("droptarget");// 2、处理拖放事件的函数function handleEvent(event) {let info = "",output = document.getElementById("output"),files, i, len;// 阻止默认事件event.preventDefault();if (event.type == "drop") {// 可以通过 event.dataTransfer.files 读到文件,是个数组files = event.dataTransfer.files;// 然后使用文件i = 0;len = files.length;while (i < len) {info += `${files[i].name} (${files[i].type}, ${files[i].size} bytes)<br>`;i++;}output.innerHTML = info;}}// 必须取消 dragenter、dragover 和 drop 的默认行为droptarget.addEventListener("dragenter", handleEvent);droptarget.addEventListener("dragover", handleEvent);droptarget.addEventListener("drop", handleEvent);
2、展示
FileReader API 读取文件并展示
1、用户选择文件后,可以通过event.target.files 获取用户选择的文件。
2、使用FileReader 对象,可以读取File类型的对象和Blob类型的对象,
(1)调用readAsDataURL方法,可以获得”data : URL”格式的字符串(base64编码),这个直接放在图片标签里的src上,就可以直接展示

(2)调用readAsText方法,可以获得文件内的文本,可以直接当做文本展示
// HTML// <input type="file" name="fileList" id="files-list">// <div id="progress">读取进度</div>// <div id="output">展示</div>let filesList = document.getElementById("files-list"); // input 选择文件按钮let progress = document.getElementById("progress"); // 显示进度let output = document.getElementById("output"); // 显示输出信息// 1、添加改变事件,也就是用户选择了文件filesList.addEventListener("change", (event) => {let info = "", //输出信息// 2、获取用户选择的文件列表,类似数组let files = event.target.files;let type = "default"; // 读取的文件类型// 3、使用FileReader API,通过创建对象实例let reader = new FileReader();// 区分文件是图片还是文本if (/image/.test(files[0].type)) {reader.readAsDataURL(files[0]); // 调用readAsDataURL方法,更适合展示图片type = "image";} else {reader.readAsText(files[0]); // 调用readAsText方法,更适合文本type = "text";}// 4、设置读取错误的方法,用于显示读取错误信息reader.onerror = function() {output.innerHTML = "无法读取文件,错误码为 " + reader.error.code;};// 5、设置读取中方法,用于显示读取中的提示,每50毫秒触发一次reader.onprogress = function(event) {// event.lengthComputable:文件大小是否是可计算的(布尔值)// event.loaded:当前已加载的字节数// event.total:总共加载的字节数if (event.lengthComputable) {progress.innerHTML = `${event.loaded}/${event.total}`;}};// 6、设置读取完成方法,用于展示结果reader.onload = function() {let html = "";switch(type) {case "image":html = `<img src="${reader.result}">`;break;case "text":html = reader.result;break;}output.innerHTML = html;};});
URL 对象展示图片
好处就是不需要先读取再展示,而是直接展示
// HTML// <input type="file" name="fileList" id="files-list">// <div id="progress">读取进度</div>// <div id="output">展示</div>let filesList = document.getElementById("filesList");let progress = document.getElementById("progress");let output = document.getElementById("output");// 1、添加改变事件,也就是用户选择了文件filesList.addEventListener("change", (event) => {let info = "";// 2、获取用户选择的文件列表,类似数组let files = event.target.files;// 3、通过URL对象展示let url = window.URL.createObjectURL(files[0]);//if (url) {if (/image/.test(files[0].type)) {output.innerHTML = `<img src="${url}">`;} else {output.innerHTML = `<a href="${url}" download="文件名.jpg">点击下载</a>`;}} else {output.innerHTML = "你的浏览器不支持 URL 对象";}});

提前释放展示的文件(可选)
页面卸载时,所有对象 URL 占用的内存都会被释放。不过,最好在不使用时就立即释放内存,以便尽可能保持页面占用最少资源。
// window.URL.revokeObjectURL(files[0]);// 可以展示完图片后释放document.getElementById('f').addEventListener('change', function (e) {var file = this.files[0];const img = document.getElementById('img');const url = window.URL.createObjectURL(file);img.src = url;img.onload = function () {// 释放一个之前通过调用 URL.createObjectURL创建的 URL 对象window.URL.revokeObjectURL(url);}}, false);
3、上传
FormData 通过 Axios 上传
现代前端开发,一般都用前端框架,如Vue、React等,会用Axios这些发送请求的第三方库。
然后创建表单数据对象FormData,给表单对象添加文件数据,然后发送
// HTML// <input type="file" name="fileList" id="files-list">import axiso from 'axios'let filesList = document.getElementById("filesList");function update(event){let file = event.target.files[0];let param = new FormData(); // 创建FormData对象param.append('file',file);// 通过append向form对象添加数据,第一个参数是key;第二个参数是value,可以传入一个Blob 或 File 类型对象// 添加请求头let config = {headers:{'Content-Type':'multipart/form-data'}};// 通过Axios模块发送请求给后端axiso.post('http://后端接口',param,config).then(response=>{console.log(response.data);})}filesList.addEventListener("change", update)
4、下载
标签下载
// 1、创建a标签let a = document.createElement('a')// 2、通过URL对象创建一个URL,赋值给a标签的地址hrefa.href = URL.createObjectURL(blob)// 模拟点击a标签弹出下载框a.dispatchEvent(new MouseEvent('click'))
第三方库 FileSaver.js
github说明:https://github.com/eligrey/FileSaver.js
原理:就是通过a标签触发点击事件下载,只是做了很多不同浏览器的边界判断
安装
npm install file-saver —save
或者cdn引入
<script src="./public/cdn/file/FileSaver.js"></script>
使用
主要是通过函数saveAs(blob , name , [选项] , [弹出框]) 进行操作
选项只有:{ autoBom: true },转换成Unicode,只有blob type 为 charset=utf-8 有效。
通过二进制格式blob,保存成文件
var blob = new Blob(["Hello, world!"], {type: "text/plain;charset=utf-8"});saveAs(blob, "hello world.txt");//其他格式也可以,比如表格xlsx
保存成图片
var canvas = document.getElementById("my-canvas")canvas.toBlob(function(blob) {saveAs(blob, "pretty image.png");});

