const reader = new FileReader();
reader.addEventListener("load", e => {
const dataURL = e.target.result;
})
reader.readAsDataURL(blob);
DataURL如何转成Blob?
function dataURItoBlob(dataURI) {
// convert base64 to raw binary data held in a string
// doesn't handle URLEncoded DataURIs - see SO answer #6850276 for code that does this
var byteString = atob(dataURI.split(',')[1]);
// separate out the mime component
var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0]
// write the bytes of the string to an ArrayBuffer
var ab = new ArrayBuffer(byteString.length);
// create a view into the buffer
var ia = new Uint8Array(ab);
// set the bytes of the buffer to the correct values
for (var i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
// write the ArrayBuffer to a blob, and you're done
var blob = new Blob([ab], {type: mimeString});
return blob;
}
前言
最近工作一直在使用vue+vux做移动端项目,有一个拍照上传照片的需求,发现vux里并没有实现,调研过非官方的vux-uploader后,感觉还不是很理想。
其实网上已经可以找到很多已经实现的成熟方案,但是在调研这个需求的时候,我发现在各种实现方案中也有一些puzzle的知识点,因此自己动手撸了一个轮子vux-uploader-component,并记录一二。
需求
组件的交互功能要求如下:
部分关键技术点的实现方案
使用html media capture调用手机端的相机
既然是在HTML5规范中,那最关心的问题肯定是兼容性了
html-media-capture
可以看到,在大部分的主流平台,兼容性还可以接受,
andriod
2-4 都支持,只是在ios
6-10支持不太好。感兴趣的可以在自己的手机上测测兼容性
html-media-capture demo
使用
ULR.createObjectURL
获取图片地址在这一块可以衍生出好几个问题:
URL.createObjectURL
和FileReader.readAsDataURL
,那应该使用哪个?为什么?IOS
中拍照获取的图片会自动旋转,为什么?怎么解决?File
和Blob
是什么关系?Blob URL
和Data URL
又有什么区别?Data URL
怎么转换成Blob
?使用
canvas
来压缩图片可以从两个方面可以进行压缩:
取一个最大宽度的限制对图片的宽高尺寸进行等比例的缩小
canvas.toDataURL
指定生成jpeg
或者webp
格式的图片,可以指定0-1之间的encoderOptions
最后生成的图片大小 = 原图大小
ratio
encoderOption
。使用
FormData
来上传这是
XHR
Level2的产物,可以方便的以键值对的形式插入。最大的优势是可以通过
XMLHttpRequest.send()
来异步提交二进制文件。后期还可以通过
Blob
的slice
来扩展分片上传功能。知识点剖析
FileReader和URL.createObjectURL的区别
关于
FileReader
和URL.createObjectURL
的用法就不详细介绍了,感兴趣的自行google。我们现在只需要知道
通过
FileReader.readAsDataURL(file)
可以获取一段data:base64
的字符串通过
URL.createObjectURL(blob)
可以获取当前文件的一个内存URL
既然这两个API都可以满足我们获取图片地址的需求,那它们之间的区别在哪呢?
1、执行时机
createObjectURL
是同步执行(立即的)FileReader.readAsDataURL
是异步执行(过一段时间)2、内存使用
createObjectURL
返回一段带hash
的url
,并且一直存储在内存中,直到document
触发了unload
事件(例如:document close
)或者执行revokeObjectURL
来释放。FileReader.readAsDataURL
则返回包含很多字符的base64
,并会比blob url
消耗更多内存,但是在不用的时候会自动从内存中清除(通过垃圾回收机制)3、兼容性
createObjectURL
支持从IE10往上的所有现代浏览器FileReader.readAsDataURL
同样支持从IE10往上的所有现代浏览器从上面答案不难看出,两者的优劣势
createObjectURL
可以节省性能并更快速,只不过需要在不使用的情况下手动释放内存base64
,则推荐使用FileReader.readAsDataURL
参考
相机拍照的图片会旋转
从上面的
createObjectURL
获取到图片的地址后,我们可以插入到页面元素的background-image
属性展示这个图片,PC端模拟器展示没有问题,但手机真机拍照得到的图片会有逆时针的90°旋转。为什么从相机拍照获取的图片会旋转呢?
是因为从相机拍照获取的图片的EXIF(Exchangeable image file format)会默认设置一个
orientation tag
:point_up_2:上图就是
orientation tag
与图片旋转角度的对应关系如何解决这个问题呢?
1、获取图片的
orientation
DataView
来获取,详情见stackoverflow高赞回答2、根据图片的
orientation
做对应的旋转参考
File和Blob的关系?Blob Url和DataURL的区别?DataURL如何转成Blob?
File和Blob的关系
从
input onchange
中返回的图片对象其实就是一个File
对象。而
Blob
对象是一个用来包装二进制文件的容器,File
继承于Blob
。FileReader
是用来读取内存中的文件的API,支持File
和Blob
两种格式。Blob Url和Data URLs的区别
Blob Url
只能在浏览器中通过URL.createObjectURL(blob)
创建,当不使用的时候,需要URL.revokeObjectURL(blobURL)
来进行释放。可以简单理解为对应浏览器内存文件中的软链接。该链接只能存在于浏览器单一实例或对应会话中(例如:页面的生命周期)
Data URLs
可以获取文件的base64
。可以通过
FileReader.readAsDataURL
获取DataURL如何转成Blob?
参考
上传进度条从一开始上传就是100%,为什么?
众所周知,目前监听上传文件进度的主流方式是使用XHR的
onprogress
事件来实现,但是为什么在我本地调试上传的时候,onprogress
只被调用了一次呢?在XHR2中有一个事件对象
ProgressEvent
,以下几种监听事件都可以获取到这个对象:abort()
方法来触发)ProgressEvent
的事件循环如下:每个请求发起后先触发
loadstart
,请求完成的flag
为false
在请求完成的
flag
设置为true
之前,以50ms的间隔来轮询触发progress
事件当请求完成时,请求完成的
flag
为true
,根据请求完成的结果状态,触发abort
,error
,load
,timeout
其中之一。请求完成后触发
loadend
至此,我们就很清楚的知道了,为什么就算我们在本地上传,并在
progress
的回调里console.log
也只执行一次的原因了:本地上传请求事件小于50ms只要将network调成slow 3G,并换一张高清像素的大图片进行上传,就可以看到
progress
事件会在上传完成之前以50ms的间隔调用。参考
最后
小小推广一波基于weui风格实现的移动端vue图片上传组件vux-uploader-component
欢迎扫码体验,Star, Issue, PR:)