#关于如何实现压缩
查阅 MDN 找到的是这个 API:
htmlCanvasElement.toBlob() 方法创造对象, 用以展示 canvas 上的图片; 这个图片文件可以被缓存或保存到本地, 由用户代理端自行决定. 如不特别指明, 图片的类型默认为 image/PNG, 分辨率为 96dpi.
- // callback 处理 blob 主要, type 图片类型, encoderOptions 图片压缩比例 0-1
- canvas.toBlob(callback, type, encoderOptions);
# 效果图
这里看到压缩效果已经由原来的 8.5Mb 到了 1.6Mb 左右, 这里有个问题 Mac 上面的压缩效果没 Win 好 (尴尬了).
# 实现代码
- <!DOCTYPE HTML>
- <HTML lang="en">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <meta http-equiv="X-UA-Compatible" content="ie=edge">
- <title>
- Document
- </title>
- <style>
- body{ padding: 20px 0; } #fileSelect{ margin-top: 10px; padding: 10px
- 5px; border: 1px solid chartreuse; border-radius: 5px; background: green;
- color: white; text-decoration: none; } #fileSelect:hover{ opacity: 0.8;
- }
- </style>
- </head>
- <body>
- <input type="file" id="fileElem" multiple accept="image/*" style="display:none"
- onchange="handleFiles(event,this.files)">
- <a href="#" id="fileSelect">
- 点击上传
- </a>
- </body>
- <script>
- Windows.onload = function() {
- var fileSelect = document.getElementById("fileSelect"),
- fileElem = document.getElementById("fileElem");
- fileSelect.addEventListener("click",
- function(e) {
- if (fileElem) {
- fileElem.click(); // input 文件上传
- }
- e.preventDefault();
- },
- false);
- }
- function handleFiles(e, files) {
- for (var i = 0; i < files.length; i++) {
- var file = files[i];
- var imageType = /^image\//;
- if (!imageType.test(file.type)) {
- continue;
- }
- console.log(file);
- const width = 500;
- const height = 300;
- const fileName = file.name;
- const reader = new FileReader();
- reader.readAsDataURL(file);
- reader.onload = event = >{
- const img = new Image();
- img.src = reader.result;
- img.onload = () = >{
- const elem = document.createElement('canvas');
- elem.width = width;
- elem.height = height;
- const ctx = elem.getContext('2d');
- // canvas 大小设置
- ctx.drawImage(img, 0, 0, width, height);
- // 植入内容
- document.body.append(elem);
- //--------- 如果不需要资源上传, 只需显示, 下面代码可以不要了 ------
- ctx.canvas.toBlob((blob) = >{
- const file = new File([blob], fileName, {
- type: 'image/jpeg',
- lastModified: Date.now()
- });
- console.log(file); // file 为压缩后的图片
- },
- 'image/jpeg', 10e-9);
- // ----------------------
- },
- reader.onerror = error = >console.log(error);
- };
- }
- }
- </script>
- </HTML>
- # 流程
- 1.new FileReader()
首先需要的是利用这个 API 来读取文件, 实例化之后采用 readAsDataURL 的方式来读取图片的资源路径, 这里的兼容性不错, 囊阔了几乎所有浏览器版本和平台.
reader.readAsDataURL(file); // 这样读取图片, 上述例子可以多选操作, 所以循环了下
2.reader.onload 的回调中创建图片
- // 创建图片内容
- const img = new Image();
- // 将图片地址绑定到 base64, 这里遇到一个问题采用 event.target.result 取不到当前, reader 下面有 result 属性可用
- img.src = reader.result;
3. 创建画布, 绘制图片内容
- // 创建 canvas 画布
- const elem = document.createElement('canvas');
- // 设置画布大小
- elem.width = width;
- elem.height = height;
- // 获取
- const ctx = elem.getContext('2d')
- // 绘制
- ctx.drawImage(img, 0, 0, width, height); // drawImage 这里查看 MDN 文档即可
- // 植入页面
- document.body.append(elem);
4. 划重点压缩, 将 canvas 压缩大小转为 image 对象
- ctx.canvas.toBlob((blob) => {
- const file = new File([blob], fileName, {
- type: 'image/jpeg', // 这里的格式可以是 jpg,PNG,gif,PNG 等, 都试了下, 压缩大小不变
- lastModified: Date.now()
- });
- console.log(file);
- }, 'image/jpeg', 10e-9); // 这里的 10e-9 很小了 极限 0-1 0 是最大压缩比率, 1 这里有点玄学变大了 超过 1 或者小于 0 大小大概压缩了 6 成左右
# 补充
这里看到一个兼容性的问题, 主要考虑的 Sarari iOS 上, 谁叫客户用呢?
- // MDN 给除了兼容代码, 大体是是在 Object 原型添加了 toBlob 函数实现, 粘贴添加就好
- if (!HTMLCanvasElement.prototype.toBlob) {
- Object.defineProperty(HTMLCanvasElement.prototype, 'toBlob', {
- value: function (callback, type, quality) {
- var dataURL = this.toDataURL(type, quality).split(',')[1];
- setTimeout(function() {
- var binStr = atob( dataURL ),
- len = binStr.length,
- arr = new Uint8Array(len);
- for (var i = 0; i < len; i++ ) {
- arr[i] = binStr.charCodeAt(i);
- }
- callback( new Blob( [arr], {type: type || 'image/jpeg'} ) );
- });
- }
- });
- }
# 结语
网上资料挺多, 没找到一下就能跑的, 查查搜搜实现了功能, 还能用, 是真的. -- 2019 年 3 月 25 日 周边无人
# 参考资料
MIME 类型
- HTMLCanvasElement.toBlob()
- Canvas API
- CanvasRenderingContext2D.drawImage()
来源: https://juejin.im/post/5c98c978f265da60f00ed943