

HTML5 图片上传解决方案
source link: https://blog.hhking.cn/2018/11/29/html5-img-upload/?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

前端做图片上传时,经常会遇到图片压缩、图片预览等需求。而这个过程中,会遇到一个个的坑。下面就来看一看 HTML5 实现图片上传的整个过程。
基本结构
图片上传是使用 input 标签来选择图片的:
<input type="file" accept="image/*">
这里可能遇到一个坑:
可能会遇到响应迟钝,文件选择框过好几秒才弹出。 具体的原因可以查看这里 。
解决的方法是将 * 通配符改成指定的 MIME 类型。例如
<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 编码。示例如下:
let fr = new FileReader(); // 读取成功回调 fr.onload = e => { // e.target.result 就是图片的 base64 地址,可以直接用于图片的 src img.src = e.target.result; } // 失败回调 fr.onerror = e => { // error handle } // 读取图片 fr.readAsDataURL(file);
方法参考: FileReader
图片压缩
前端进行图片压缩,不但节省流量,而且加速上传速度,提高用户体验。
上一步读取了图片,就可以对图片进行操作了。前端实现图片压缩,原理很简单:
- 缩小图片,大图片转成小图片
- 降低图片质量
第一点是通过 canvas
的 drawImage()
方法来实现,第二点在后面会提到。
void ctx.drawImage(image, dx, dy, dWidth, dHeight)
是在 canvas 上绘制图像,我们只需要把原图,在 canvas 上绘制成更小的图片,就实现了压缩,就是这么简单。
所以关键就是怎么来设置 dWidth
和 dHeight
。
一般的做法是限制图片的最大长度和宽度,超过则等比例缩放。例如:
// width height 图片长宽 // maxWidth maxHeight 限制的图片最大长宽 let scale = width / height; if (scale >= maxWidth / maxHeight) { if (width > maxWidth) { height = maxWidth / scale; width = maxWidth; } } else if (height > maxHeight) { width = maxHeight * scale; height = maxHeight; } // 这里的 image 就是上一步得到的图片内容 ctx.drawImage(image, 0, 0, width, height);
方法参考: CanvasRenderingContext2D.drawImage()
图片输出
上一步在 canvas 绘画了图片,接下来就需要把 canvas 画布转化成 img 图像。
canvas
提供了两个转图片的方法:
- HTMLCanvasElement.toDataURL() :图片转换成base64格式
- HTMLCanvasElement.toBlob() :图片转换成Blob文件
toDataURL()
canvas.toDataURL(type, encoderOptions);
属于同步方法,返回 base64 格式的图片。
第一个参数 type
是图片格式;
第二个参数 encoderOptions
就是用于之前提到的,控制图片质量,达到压缩图片的效果。
在指定图片格式为 image/jpeg 或
image/webp的情况下,可以从 0 到 1 的区间内选择图片的质量
。如果超出取值范围,将会使用默认值 0.92
。
toBlob()
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)
方法可以得到 base64 -
URL.createObjectURL(Blob|File)
返回的 URL 可以作为图片的链接
详情参考:
手机照片旋转问题
把在 iPhone 上拍出来的照片,通过上面的方式进行上传,你会发现图片方向和你预料的不同。
orientation
使用 iPhone 拍照片,会根据你拍照时手机的方向,照片会有不同的方向。这个方向可以通过图片的 orientation
参数来确定。
可以通过 exif-js 来获取图片的 orientation
:
import EXIF from 'exif-js'; // file 是上面提到的 File 文件 EXIF.getData(file, function() { var orientation = EXIF.getTag(this, 'Orientation'); });
详情参考: 如何处理iOS中照片的方向
校正方向
要校正图片的方向,只需要根据 orientation
参数,把图片的方向旋转会正常即可。旋转需要用到 canvas 的 rotate()
方法。
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)
旋转之后,原有的画布不变,坐标跟着旋转,图片转到可视范围之外,所以:
- 需要把图片移到可视范围里
- 调整画布大小
// other code... case 6: // 旋转角度 degree = 90; // 调整画布大小 canvas.width = imgHeight; canvas.height = imgWidth; // 修改绘画位置 imgHeight = - imgHeight; break; // other code ... // canvas 旋转 ctx.rotate((degree * Math.PI)/180); // 绘制图片 ctx.drawImage(image, 0, 0, imgWidth, imgHeight);
其他的 orientation
也是类似的原理。 查看示例代码
总结
图片压缩上传的过程,总结起来就是:图片 → 压缩 → 图片。
这个过程中,核心点是:
-
FileReader API 使用
-
Canvas 实现图片压缩、绘制和方向校正
这里主要总结了使用 HTML5 API 实现图片上传的过程,在实际的使用还要根据具体的使用场景,考虑兼容问题,选择合适的解决方案。
最后,查看完整的示例代码: h5ImgCompress
Recommend
-
137
js上传图片 额 呆坐许久 感觉 有很多想写的 就是不知从何写起。。贼尴尬。 其...
-
79
-
91
Picdiet.js - JS 图片上传组件,支持图片上传前压缩体积、缩放尺寸和裁剪 - NEXT
-
65
-
8
一、input:file属性 属性值有以下几个比较常用: accept:表示可以选择的文件MIME类型,多个MIME类型用英文逗号分开,常用的MIME类型见下表。 multiple:是否可以选择多个文件,多个文件时其value值为第一个文件的虚拟路径。 1...
-
5
by zhangxinxu from http://www.zhangxinxu.com/wordpress/?p=6308 本文可全文转载,但需得到原作者书面许可,同时保留原作者和出处,摘要引流则随...
-
7
关于图片img的响应属性srcset,和sizes及picture,很久之前看过,但是因为浏览器兼容原因,一直没有在项目中使用,但是最近发现,这几个属性兼容性还可以了,可以去caniuse中看一下,基本可以在项目中使用了。今天,简单介绍一下这几个属性。 srcset 和...
-
10
by zhangxinxu from http://www.zhangxinxu.com 本文地址:http://www.zhangxinxu.com/wordpress/?p=1923
-
11
HTML5文件API之图片预览 浏览:3300次 出处信息 图片上传在当今的Web应用中是一个非常常用的功能,如果不需要...
-
4
前段时间,项目中用到html5音频采集,主要是和微信录音一样,流程是按住说话,右侧滑动可以音频转文字,左侧滑动撤销。关于按住说话及左右侧滑动交互,相对简单,主要是运用了onTouchStart,onTouchMove,onTouchEnd三个事件完成。我之前文章也有过
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK