楼主最近项目结束无所事事=。=,加上之前用element的上传组件,用着有一点点不爽…就试着自己写了下上传组件,啊,写完的心理活动就是,虽然一直吐槽element的upload,但是真正轮到自己写才发现too young =。=。他们还是挺厉害的,特别是生命周期的封装上。
回归正题,楼主的组件,参照着element和ant-design的upload api,大致实现了如下功能:
- 单文件/多文件上传
- 文件预览,可选预览方式:列表/卡片,卡片主要是针对img(用URL.createObjectURL)
progress进度条
代码中实际上有两种:- 一种是读到内存,用fileReader的progress事件
还有一种是xhr的upload.onprogress事件
最终进度条是按照post到服务器的进度来显示的)
- accept 限定所能接受的文件类型
- 可以支持拖拽上传、粘贴上传
源码请戳upload组件,upload分支哟
1. fileReader
内置了几个事件,包括onprogress,onloadstart,onabort,onerror,onload,onloadend等几个事件,其中onloadend类似于finally操作,不管onerror还是onload,结束都会执行。在这边我们需要处理进度条,那么先看onprogress事件。event对象有这么几个有用的属性:
- lengthComputable:决定浏览器是否可以对进度进行计算
- total: 文件大小
- loaded:已经上传的大小
- result: 读取的结果,以data:url格式,可以用来作为src实现预览功能,当然我们也可以对file对象直接用URL.createObjectURL())来获取实现。
2. 文件上传
新版本的XMLHttpRequest对象,传送数据的时候,有一个progress事件,用来返回进度信息。
它分成上传和下载两种情况。下载的progress事件属于XMLHttpRequest对象,上传的progress事件属于XMLHttpRequest.upload对象。
|
|
要显示进度的话,可以在onprogress事件中获取信息。具体的跟上面一样。主要就是xhr和filereader其实都实现了progressEvent。具体的可以通过给event对象打log看下event.target就知道了。
在filereader的onprogress中,e.target就是filereader,所以当freader.onload事件中可以调用e.target.result来获取src实现预览。如下:
|
|
|
|
3. xhr
执行顺序:
|
|
只有当 responseType 为”text”、””时,xhr对象上才有此属性,此时才能调用xhr.responseText
从上面介绍的事件中,可以知道若xhr请求成功,就会触发xhr.onreadystatechange和xhr.onload两个事件。 一般倾向于 xhr.onload事件,因为xhr.onreadystatechange是每次xhr.readyState变化时都会触发,而不是xhr.readyState=4时才触发。
|
|
补充一条,xhr.send在xmlhttprequest2中data type其实是可以有formdata的,这个就有利于我们上传文件。它会自动将文件转变为字节流对象,然后后台可以进行解析。如果是单纯的obj data,里面放 file的话,无法传输过去。也没法解析。
4. html5 drag drop
- 一定要在dragover的处理事件中preventDefault(),否则默认行为是会跳转到另一个页面预览文件的。
- 获取文件列表: e.dataTransfer.files
- 不会触发到input的change事件
5. file上传
|
|
|
|
一般是把input进行隐藏,用另一个自定义样式的div代替它,然后在click事件中去手动click该input。
而监听文件变化的事件 onchange事件的参数是本地新变更(增加的files数组)。
6. 粘贴上传
我们需要对items的每一项转化为file对象。
|
|
然后就可以继续用之前的方法(URL.createObjURL(file))读取,预览等等操作。
其实它内置了两个方法,一个是getAsFile,一个是getAsString。转换完后会成为blob对象(file也是一个blob对象)。
注意点: 楼主本来是想粘贴完以后清空剪贴板的。但是貌似看了下w3c的文档,只有在read/write mode下才可以进行操作,而read对应于drag event,write对应于drop事件,其他对应于protected mode。
而平时我们所说的比如知乎的剪贴版权信息,则是用e.clipboardData.setData()这些来进行操作的。既然可以set,那么也可以用它来清空。e.clipboardData.clearData(“text”)也可以实现。