简介

本文结合实际项目场景,记录图片上传时的一种本地压缩预览解决方案。这里的本地预览是指,页面上的图片是读取的本机资源进行展示,而没有通过网络请求加载。

实际的项目场景

在这阵子的项目开发中需要上传图片。需求很简单,能上传图片,并按如下UI效果展示即可。

clipboard.png

图1 上传完成后的效果

开发时,我首先是对新选择图片的上传进度进行了加载展示,等到单个图片上传完成,调取相应api拿到图片的存储url,然后根据url加载图片展示在界面。像这样:

clipboard.png

图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 变成了这样子:

clipboard.png

图3: 改进之后的uploading UI

继续改进 – 图片压缩

至此似乎已经解决问题了,但考虑到该业务场景下,图片都是以小图展示的,所以又想着可以把图片压缩一下再预览,毕竟FileReader的readAsDataURL方法是直接以base64对图片进行编码的,不压缩的话吃内存较多。

压缩是借助canvas画布技术来做的。大体步骤就是将上传的大图画到一张小的画布上,然后将新绘制的小画布导出成data URL作为输入结果。这样就在前端实现了简单的图片压缩处理。

备注

该方案尚存诸多不足之处,比如最起码的FileReader兼容性问题,在IE10以下版本就不支持。网上对IE旧版本建议用「滤镜」来兼容。 欢迎讨论。


你可
92 声望1 粉丝

like a fighter