浏览器图片压缩上传
需求
客户端上传图片到服务器。大部分情况下,客户端的图片质量,远大于业务实际需要。上传,存储和下载展示,多出的都是浪费,节能环保,从源头做起。
实现原理
实现图片的压缩,实际上就是改变图片的尺寸(宽和高),或者改变图片的体积。使用CanvasRenderingContext2D.drawImage()和HTMLCanvasElement.toDataURL()这两个API,就可以实现这两种操作。CanvasRenderingContext2D.drawImage() 可以将Image写入到canvas中,写入过程中可以自定义画布上绘制图像的宽度和高度,从而实现对图片尺寸的改变。HTMLCanvasElement.toDataURL()可以将canvas的内容,导出成图片,导出时可以指定图片的质量,从而改变图片的体积。
但是我们从input得到的文件数据是File类型的,需要转变成CanvasRenderingContext2D.drawImage()能够写入的HTMLImageElement,HTMLCanvasElement.toDataURL()导出的数据格式是DataURL,而我们上传时需要Blob类型的数据,因此要实现得到文件,压缩,然后上传,我们需要对数据做一系列的处理。如同流水线加工,塞进去一只活蹦乱跳的大母鸡,最后得到一盒全家桶。
实现过程
实现过程分为三个部分:
- 得到目标文件
- 压缩
- 上传
第一步:得到目标文件
var input = document.getElementById('file'); input.addEventListener('change',function(e){ var file = this.files[0]; fileName = file.name; });
第二步:压缩
压缩过程中,图片对应的数据类型在不断变化,按照类型变化,分为7个步骤
- File转化成Data URIs
- Data URIs转化成HTMLImageElement
- HTMLImageElement写入canvas
- canvas转化成DataURL
- DataURL转化成二进制String
- 二进制String转化成ArrayBuffer
- ArrayBuffer转化成Blob
1.File转化成Data URIs
function fileToDataURL(file,callback) { var reader = new FileReader(); reader.onload = function () { var result = this.result; callback(result); } reader.readAsDataURL(file); }
2.Data URIs转化成HTMLImageElement
function dataURLToImage(dataURL,callback) { var img = new Image(); img.onload = function() { callback(img); } img.src = dataURL; }
3.HTMLImageElement写入canvas
创建canvas之后,可以设置canvas的宽和高。图片写入canvas的过程中,可以指定写入后的宽和高。这个过程,就是对图片尺寸进行更改。
4.canvas转化成DataURL
这个过程中需要注意,canvas.toDataURL(type, encoderOptions); 当type为''image/jpeg''或者'image/webp'时,encoderOptions指定的图片质量系数才是有效的。
function compress(img) { var canvas = document.createElement('canvas'), width = img.width, height = img.height; canvas.width = width; canvas.height = height; var context = canvas.getContext('2d'); context.drawImage(img, 0, 0, width, height); var dataUrl = canvas.toDataURL('image/jpeg',0.7); return dataUrl; }
5.DataURL转化成二进制String
6.二进制String转化成ArrayBuffer
7.ArrayBuffer转化成Blob
function dataUrlToBlob(dataUrl) { var text = window.atob(dataUrl.split(",")[1]); var buffer = new ArrayBuffer(text.length); var ubuffer = new Uint8Array(buffer); for (var i = 0; i < text.length; i++) { ubuffer[i] = text.charCodeAt(i); } var blob; var type = 'image/jpeg'; blob = new window.Blob([buffer], {type: type}); return blob; }
第三步:上传
得到图片对应的Blob数据,使用FormData发送到服务端。
function upload(blob,name) { var formData = new FormData(); name = name.substring(0,name.lastIndexOf('.')) + '.jpeg'; formData.append('file', blob ,name); formData.append('name',name); formData.append('size',blob.size); formData.append('type','image/jpeg'); var xhr = new XMLHttpRequest(); xhr.open('post','/fedemos/server/fileupload.php'); xhr.onreadystatechange = function () { if (xhr.readyState == 4 && xhr.status == 200) { console.log() } } xhr.send(formData); }
代码示例
<!DOCTYPE HTML> <html> <head> <meta charset="UTF-8"> <title></title> <meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no, minimum-scale=1.0, maximum-scale=1.0"> </head> <body> <input id="file" type="file" name="file" > <script type="text/javascript"> var input = document.getElementById('file'); input.addEventListener('change',function(e){ var file = this.files[0]; fileName = file.name; imageCompress(file,function(blob){ upload(blob,fileName); }) }); function imageCompress(file,callback) { fileToDataURL(file,function(dataURL){ dataURLToImage(dataURL,function(img){ transform(img) }) }) function fileToDataURL(file,callback) { var reader = new FileReader(); reader.onload = function () { var result = this.result; callback(result); } reader.readAsDataURL(file); } function dataURLToImage(dataURL,callback) { var img = new Image(); img.onload = function() { callback(img); } img.src = dataURL; } function transform(img) { var dataUrl = compress(img); var blob = dataUrlToBlob(dataUrl); callback(blob); } function compress(img) { var canvas = document.createElement('canvas'), width = img.width, height = img.height; // if(width>1280 && width>height) { // height = height*1280/width; // width = 1280; // } else if(height>width && height>1280) { // width = width*1280/height; // height = 1280; // } canvas.width = width; canvas.height = height; var context = canvas.getContext('2d'); context.drawImage(img, 0, 0, width, height); var dataUrl = canvas.toDataURL('image/jpeg',0.7); return dataUrl; } function dataUrlToBlob(dataUrl) { var text = window.atob(dataUrl.split(",")[1]); var buffer = new ArrayBuffer(text.length); var ubuffer = new Uint8Array(buffer); for (var i = 0; i < text.length; i++) { ubuffer[i] = text.charCodeAt(i); } var blob; var type = 'image/jpeg'; blob = new window.Blob([buffer], {type: type}); return blob; } } function upload(blob,name) { var formData = new FormData(); name = name.substring(0,name.lastIndexOf('.')) + '.jpeg'; formData.append('file', blob ,name); formData.append('name',name); formData.append('size',blob.size); formData.append('type','image/jpeg'); var xhr = new XMLHttpRequest(); xhr.open('post','/fedemos/server/fileupload.php'); xhr.onreadystatechange = function () { if (xhr.readyState == 4 && xhr.status == 200) { console.log() } } xhr.send(formData); } </script> </body> </html>