“ 所有轮子都造好了已经~”
目录
01
—
选择
直接上代码,原生的,非类库的:
<input type="file" accept=".jpg, .jpeg, .png" capture="user" multiple />
进入 Html5 时代,在页面中开启上传功能,只需要一个 input 组件。type 代表上传,accept 代表接受的文件类型,capture 代表从摄像头拍照获取,capture="user" 代表默认打开前置摄像头,multiple 代表一次可上传多个文件。
02
—
剪切板上传与拖拽上传
在可编辑区域开启contenteditable,为编辑区绑定paste事件,然后处理paste 事件,从 event.clipboardData || window.clipboardData 获得数据,接着将该数据转换为文件items[i].getAsFile()。拿到 File 后,与选取就是一样的处理逻辑了。
拖拽操作的实现是类似的。定义一个允许拖放文件的区域,通过 e.preventDefault() 为该区域取消drop 事件的默认行为。监听鼠标在拖拽区域上 的 dragover 及鼠标离开拖拽区域 dragleave 事件。在拖拽区域上释放文件发生 drop 事件时,通过 e.dataTransfer.files 获得文件信息。拿到 File 后,与前面处理逻辑一样。
03
—
预览
以前在页面上实现预览功能,要么将图片上传到服务器上,拿到 url 再拉回本地,赋值给 img 显示;要么使用 Flash 技术。
现在好了,可以直接使用 URL.createObjectURL 将 input[file] 选择的文件在用户本地转化为一个 可以赋值给 img 组件 src 属性的图片地址。
const $ = document.getElementById.bind(document);...$inputFile.addEventListener('change', function() {const file = this.files[0]; $previewImage.src = URL.createObjectURL(file);}, this);
这段代码看起来很简洁,$ 绑定在 document 对象上,不引入 JQuery 或 Zepto,也可以优雅地查找页面元素。使用 addEventListener 监听 input[file] 的 change 事件,当用户选择图片文件时,调用 URL.createObjectURL(file) 将 File 对象转化为可被 img 组件接受并预览的图片地址。
除了 使用 URL.createObjectURL,还可以使用 FileReader 。
const file = this.files[0];const reader = new FileReader();reader.addEventListener('load', function() { $previewImage.src = reader.result;}, false);
if(file) { reader.readAsDataURL(file);}
FileReader 是异步的,且只能加载安全沙箱之内的用户内容,它并不能像其它语言的 file 模块那样在用户的文件系统中按路径随意读取文件。
实现预览功能,直接使用 URL.createObjectURL 实现即可,更简洁。
04
—
裁剪
实现选取、裁剪图片部分区域的功能。直接使用下面这个库:
https://github.com/fengyuanchen/cropperjs
可定制,基本能满足所有需求;也可以基于 MIT 协议修改。
function showCropper() {$cropper.style.display = 'block';cropperInstance && cropperInstance.destroy();cropperInstance = new Cropper($cropperImage, { viewMode: 1, aspectRatio: 1, autoCropArea: 1, dragMode: 'move', guides: false, highlight: false, cropBoxMovable: false, cropBoxResizable: false});}
05
—
上传
使用 new FormData 可以直接构建表单数据对象,然后发送给服务器:
const formData = new FormData();formData.append('avatar', blob);fetch('server url', { method: 'POST', body: formData});
旧方式拼接 XMLHttpRequest 请求体的朝代已经一去不复返了:
06
—
断点续传
有时候文件比较大,或网络环境不稳定,需要实现分片上传及断点续传;有时候单纯为了追求上传速度,希望将浏览器 6 个并发连接跑满,所以也希望实现分片上传。
方案很简单。从 input[file] 选择拿到的对象是一个 File 对象,它继续于 Blob。Blob
对象表示一个不可变、原始数据的类文件对象,并且有一个 slice 方法,可以将自身分割为多个小段的 Blob 对象。每个小段 Blob 是可以单独发给服务器的。
let chunkSize=2*1024*1024;//分片大小 2Mlet file = document.getElementById('f1').files[0];if(file.size>chunkSize){let start=0,end=0;while (true) { end += chunkSize;let blob = file.slice(start,end); start+=chunkSize; ... chunks.push(blob);//保存分段数据 }}else{ chunks.push(file.slice(0));}
截段之后,还需要一个 js-spark-md5 类库:
https://github.com/satazor/js-spark-md5
这是一个在本地就可以给 Blog 对象签名生成唯一标识的类库,只要上传的是同一个文件,只要分段大小一致,网络断了再次上传,生成的标识也是一样的。每次上传完一个切片,就将标识存储在本地,页面刷新后先看看本地哪些分片已经上传过了。
— END —
2019 年冬天于北京
▼
往期精选
▼