前言
前端做图片上传时,经常会遇到图片压缩、图片预览等需求。而这个过程中,会遇到一个个的坑。下面就来看一看 HTML5 实现图片上传的整个过程。
基本结构
图片上传是使用 input 标签来选择图片的:
1 | <input type="file" accept="image/*"> |
这里可能遇到一个坑:
可能会遇到响应迟钝,文件选择框过好几秒才弹出。具体的原因可以查看这里。
解决的方法是将 * 通配符改成指定的 MIME 类型。例如
1 | <input type="file" accept="image/gif,image/jpeg,image/jpg,image/png"> |
获取图片文件
通过监听 input 的 change 事件,获取 FileList类数组对象(event.target.files)。FileList 对象的成员就是 File对象,包含的属性如图:

FileReader
要对图片进行压缩,首先要获取图片内容。
这里需要使用 FileReader的readAsDataURL(Blob|File) 来读取图片内容。readAsDataURL方法返回 data URL,将文件进行 base64 编码。示例如下:
1 | let fr = new FileReader(); |
方法参考:FileReader
图片压缩
前端进行图片压缩,不但节省流量,而且加速上传速度,提高用户体验。
上一步读取了图片,就可以对图片进行操作了。前端实现图片压缩,原理很简单:
- 缩小图片,大图片转成小图片
- 降低图片质量
第一点是通过 canvas 的 drawImage()方法来实现,第二点在后面会提到。
1 | void ctx.drawImage(image, dx, dy, dWidth, dHeight) |
是在 canvas 上绘制图像,我们只需要把原图,在 canvas 上绘制成更小的图片,就实现了压缩,就是这么简单。
所以关键就是怎么来设置 dWidth 和dHeight。
一般的做法是限制图片的最大长度和宽度,超过则等比例缩放。例如:
1 | // width height 图片长宽 |
图片输出
上一步在 canvas 绘画了图片,接下来就需要把 canvas 画布转化成 img 图像。
canvas提供了两个转图片的方法:
- HTMLCanvasElement.toDataURL():图片转换成base64格式
- HTMLCanvasElement.toBlob():图片转换成Blob文件
toDataURL()
1 | canvas.toDataURL(type, encoderOptions); |
属于同步方法,返回 base64 格式的图片。
第一个参数 type是图片格式;
第二个参数 encoderOptions 就是用于之前提到的,控制图片质量,达到压缩图片的效果。
在指定图片格式为
image/jpeg 或image/webp的情况下,可以从 0 到 1 的区间内选择图片的质量。如果超出取值范围,将会使用默认值0.92。
toBlob()
1 | void canvas.toBlob(callback, type, encoderOptions); |
属于异步方法,所以有个callback 参数。
type 参数指定图片格式;
encoderOptions参数指定图片质量,用于压缩图片
值在0与1之间,当请求图片格式为
image/jpeg或者image/webp时用来指定图片展示质量。
关于 Blob:
Blob对象表示一个不可变、原始数据的类文件对象。Blob 表示的不一定是JavaScript原生格式的数据。File接口基于Blob,继承了 blob 的功能并将其扩展使其支持用户系统上的文件。JavaScript 中 Blob 对象: 这篇文章介绍了 Blob,里面也提到了大文件分割上传的实现。
一般来说,对比 Blob 文件和 base64 ,有下面几点优点:
- 二进制文件,对后端更友好
- base64 字符串一般都非常长,会有性能等问题
所以选择转化成 Blob 文件进行上传更好。把 base64 或者 Blob 文件加入 FormData 里就可以实现上传了。
图片预览
一般图片上传还会要求实现图片上传进度、图片预览功能。
关于图片预览的实现,可以通过下面的方法,来获取图片链接,做为本地预览:
base64 可以作为图片链接,
FileReader.readAsDataURL(Blob|File)方法可以得到 base64URL.createObjectURL(Blob|File)返回的 URL 可以作为图片的链接
详情参考:
手机照片旋转问题
把在 iPhone 上拍出来的照片,通过上面的方式进行上传,你会发现图片方向和你预料的不同。
orientation
使用 iPhone 拍照片,会根据你拍照时手机的方向,照片会有不同的方向。这个方向可以通过图片的 orientation参数来确定。
| 旋转角度 | 参数值 | 手机方向 |
|---|---|---|
| 0° | 1 | home 键在右方的横屏拍摄方式 |
| 逆时针90° | 6 | home键在下方(正常拿手机的方向) |
| 顺时针90° | 8 | home键在上方 |
| 180° | 3 | home键在左侧 |
可以通过 exif-js 来获取图片的 orientation:
1 | import EXIF from 'exif-js'; |
详情参考:如何处理iOS中照片的方向
校正方向
要校正图片的方向,只需要根据 orientation 参数,把图片的方向旋转会正常即可。旋转需要用到 canvas 的 rotate()方法。
1 | void ctx.rotate(angle); |
参数 angle 是顺时针旋转的弧度,旋转中心点是 canvas 的起始点。
角度值换算弧度的公式: angle = degree * Math.PI / 180
以 orientation等于 6 时为例,也就是图片逆时针旋转了 90°,要把图片校正方向,就要画布顺时针旋转 90° : rotate((90 * Math.PI) / 180)

画布旋转之后,drawImage()根据画布的位置进行调整,如上图所示:
旋转之前:drawImage(image, 0, 0, x, y)
旋转之后,原有的画布不变,坐标跟着旋转,图片转到可视范围之外,所以:
- 需要把图片移到可视范围里
- 调整画布大小
1 | // other code... |
其他的 orientation 也是类似的原理。查看示例代码
总结
图片压缩上传的过程,总结起来就是:图片 → 压缩 → 图片。
这个过程中,核心点是:
FileReader API 使用
Canvas 实现图片压缩、绘制和方向校正
这里主要总结了使用 HTML5 API 实现图片上传的过程,在实际的使用还要根据具体的使用场景,考虑兼容问题,选择合适的解决方案。
最后,查看完整的示例代码:h5ImgCompress
- 本文链接: https://blog.hhking.cn/2018/11/29/html5-img-upload/
- 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!

