简介
本文结合实际项目场景,记录图片上传时的一种本地压缩预览解决方案。这里的本地预览是指,页面上的图片是读取的本机资源进行展示,而没有通过网络请求加载。
实际的项目场景
在这阵子的项目开发中需要上传图片。需求很简单,能上传图片,并按如下UI效果展示即可。
图1 上传完成后的效果
开发时,我首先是对新选择图片的上传进度进行了加载展示,等到单个图片上传完成,调取相应api拿到图片的存储url,然后根据url加载图片展示在界面。像这样:
图2. 原始的uploading UI
不足之处
以上是原始开发方案,弄完之后,我发现这样有两个比较大的问题:
图片需要等到上传结束,并拿到下载url才能展示在界面,而上传过程中,则看不到图片的样子(如上图2). 这样用户只能在上传完成后才能检查是否选择正确。
图片上传完后,需要加载url以显示图片,相当于上传后又从存储服务器下载了一遍。这不仅占用网络资源,而且会导致「上传完毕到图片展示出来 两者之间的卡顿」,因为加载图片需要时间。
考虑到这些问题,决定采用本地图片进行预览。 这样上面两个问题都能解决——上传过程中能预览图片、上传完成后不需再从服务器加载 。
解决过程
接下来说明将待上传的本地图片展示到界面中。
这里我使用的是HTML5的FileReader对象来读取选中的图片数据,FileReader对象提供的API中有一个是「readAsDataURL」,顾名思义,就是读取文件内容并处理成一种特殊的URL地址,该URL地址能直接加载到页面中, 比如赋值给img元素的src属性。
现在假设已经获取到了图片的file source——比如从<input type=’file’ />中的files,然后通过如下简单的几行代码就能读取文件:
export const readFile = (file)=>{
return new Promise((resolve)=>{
let reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = function(){
//读取完会触发unload事件, 同时result属性就是需要的结果,它的格式为「data:URL」
// do something with this.result
// e.g: resolve(this.result)
//...
});
};
});
};
将上诉结果作为图片加载到上传页面中,这样,在上传之前,就能先读取图片并展示到页面中了。uploading UI 变成了这样子:
图3: 改进之后的uploading UI
继续改进 – 图片压缩
至此似乎已经解决问题了,但考虑到该业务场景下,图片都是以小图展示的,所以又想着可以把图片压缩一下再预览,毕竟FileReader的readAsDataURL方法是直接以base64对图片进行编码的,不压缩的话吃内存较多。
压缩是借助canvas画布技术来做的。大体步骤就是将上传的大图画到一张小的画布上,然后将新绘制的小画布导出成data URL作为输入结果。这样就在前端实现了简单的图片压缩处理。
备注
该方案尚存诸多不足之处,比如最起码的FileReader兼容性问题,在IE10以下版本就不支持。网上对IE旧版本建议用「滤镜」来兼容。 欢迎讨论。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。