图片上传与预览及压缩
介绍一下如何用input标签实现图片上传和在网页上实现图片预览,使用canvas的toDataURL()方法进行压缩以及把base64转化成二进制文件数据进行上传。
可以点击这里看看效果。
目录:
input标签
//html代码
<div class="image-warpper">
<img id="preview-image" src="img/icon_add.png" class="hide">
<input type="file" class="file-input" name="file-input" accept="image/gif,image/jpeg,image/jpg,image/png,image/svg" value="">
</div>
这里用了img标签用于图片预览,使用input标签用于上传图片,所以将其 type 属性设置为“file”,设置其 accept 属性规定能够通过文件上传进行提交的文件类型,我们规定它能接受图片格式为 gif 、 jpeg 、 jpg 、 png 和 svg 的图片。
值得注意的是,一开始我 accept 属性设置为 “accept=”image/*”,因为这样可以不限制图像的格式,但是在实际使用中发现,这段代码在Chrome和Safari等Webkit浏览器下却出现了响应滞慢的问题,要等一段时间才能弹出文件选择对话框,所以就换成上面的写法,这样就解决了响应滞慢的问题。
美化图片上传样式
/* css代码(由Sass编译过来的) */
.image-warpper {
position: relative;
width: 100px;
height: 100px;
border: solid 1px #b3b3b3;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
margin: 0 auto;
cursor: pointer;
text-align: center;
line-height: 130px;
background: url(../img/icon_add.png) no-repeat;
-webkit-background-size: 100%;
-moz-background-size: 100%;
background-size: 100%; }
.image-warpper input {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
z-index: 100;
cursor: pointer;
-webkit-appearance: none;
-webkit-opacity: 0;
-moz-opacity: 0;
-o-opacity: 0;
-ms-opacity: 0;
opacity: 0;
filter: alpha(opacity=0); }
.image-warpper img {
position: absolute;
max-width: 100%;
max-height: 100%;
z-index: 10;
top: 50%;
left: 50%;
-webkit-transform: translate(-50%, -50%);
-moz-transform: translate(-50%, -50%);
transform: translate(-50%, -50%); }
.hide {
display: none; }
不得不说html原来的input标签样式实现无法接受,所以我们可以把它设置为透明的。
来看看效果吧。
图片上传前:
图片预览效果:
为input标签绑定onchange事件
//js代码(使用jQuery)
$(".file-input").on("change", function(){
//获取input标签的兄弟节点(用于图片预览)
var $preview = $(this).siblings("img");
//获取用户上传的图片
var files = !!this.files ? this.files : [];
//当没有上传图片或者浏览器不支持FileReader的时候
if (!files.length || !window.FileReader) {
//add your codes here...
return false;
};
//当上传的文件格式是image的时候
if (/image\/\w+/.test(files[0].type)){
var reader = new FileReader();
reader.readAsDataURL(files[0]);
var fileType = files[0].type;
reader.onloadend = function(){
//实现预览
//preview($preview, reader.result);
//实现压缩
//compress(reader.result, fileType);
}
}else{
//当上传的文件不是图片的时候
//add your codes here...
}
});
每次输入框发生改变的时候(即用户点击上传按钮),上传的图片会保存在input标签的“files”FileList文件列表对象里面,因为我们只上传了一张图片,所以上传的图片就是files的第一个元素(即files[0])。我们可以看一下files[0]到底是什么:
首先验证一下用户是否上传了图片和验证一下浏览器是否支持FileReader对象,不支持的话——是时候换一个浏览器了。
FileReader 对象允许Web应用程序异步读取存储在用户计算机上的文件(或原始数据缓冲区)的内容,使用 File 或 Blob 对象指定要读取的文件或数据。其中File对象可以是来自用户在一个元素上选择文件后返回的FileList对象.
接着用正则表达式判断一下上传的文件的类型是否图片类型,如果是,则使用 FileReader 的 readAsDataURL() 方法读取图片。
该方法会读取指定的 Blob 或 File 对象,读取操作完成的时候,readyState 会变成已完成(DONE),并触发 loadend 事件,同时 result 属性将包含一个data:URL格式的字符串(base64编码)以表示所读取文件的内容。(关于FileReader对象的知识可以看看这里)。
实现图片预览
//js代码(使用jQuery)
function preview($preview, image){
$preview.attr("src",image).show().removeClass("hide");
$preview.closest('.image-warpper').css({'background':'#f5f5f5','border':'2px dashed #ccc'})
}
预览图片其实很简单,把使用 FileReader 的 readAsDataURL() 方法获取到的 data:URL 格式的字符串当成参数传给 preview 函数(就是这里的“image”),然后赋值给 img 标签(这里的 $preview 节点)的 src 属性就可以了,然后使我们一开隐藏起来的用于预览 img 标签显示出来,再给最外层的 div 加点样式就大功告成了。
- attr(name|properties|key,value|fn) 设置或返回被选元素的属性值。
- show([speed,[easing],[fn]]) 显示隐藏的匹配元素。
- removeClass([class|fn]) 从所有匹配的元素中删除全部或者指定的类。
- closest(expr|object|element) 从元素本身开始,逐级向上级元素匹配,并返回最先匹配的元素。
- css(name|pro|[,val|fn]) 访问和设置匹配元素的样式属性。
压缩图片
function compress(uncompressed, fileType){
var image= new Image();
image.src = uncompressed;
image.onload = function(){
var canvas = document.createElement('canvas');
//如果图片大于四百万像素,计算压缩比并将大小压至400万以下
var ratio;
if ((ratio = this.width * this.height / 4000000)>1) {
ratio = Math.sqrt(ratio);
this.width /= ratio;
this.height /= ratio;
}else {
ratio = 1;
}
canvas.width = this.width;
canvas.height = this.height;
var ctx = canvas.getContext('2d');
ctx.drawImage(this, 0, 0, canvas.width, canvas.height);
//压缩图片(压缩质量可以根据实际情况调整)
var newImage = canvas.toDataURL(fileType, 0.8);
//转化成二进制文件数据并存入 FormData
var fd = new FormData();
var blob = dataURItoBlob(newImage);
fd.append('file', blob);
}
}
//base64 转 二进制文件
function dataURItoBlob(dataURI) {
var byteString = atob(dataURI.split(',')[1]);
var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
var ab = new ArrayBuffer(byteString.length);
var ia = new Uint8Array(ab);
for (var i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
return new Blob([ab], {type: mimeString});
}
首先使用 canvas 的 drawImage(imageObject, x, y, width, height)方法在画布上绘制图片,值得注意的是, drawImage() 的第一个参数是一个 Image,而并不是直接传入图片的 src 。
当 Image 对象加载完成之后,会触发 onload 事件,我们将在 onload 事件中绘制图片。
在绘制图片之前我们首先对把超过400万,像素的图片压缩到400万像素以下,然后调用 drawImage() 方法绘制图片,绘制完图片之后,我们就可以压缩图片了。
因为在 IOS 中 canvas 的大小有限制,即如果 canvas 的大小大于大概五百万像素(即宽高乘积)的时候,不仅图片画不出来,其他什么东西也都是画不出来的,所以我们需要先把图片按比例压缩到400万像素以下。
最后调用toDataURL(type, encoderOptions)方法对图片进行压缩,第一个参数是压缩的类型,第二个参数是图片的质量。
但是它返回的依旧是一个 base64 编码的图片,所以我们还需要使用 dataURItoBlob 函数把它转化成二进制文件数据(当然,我们也可以直接上传base64编码的图片,即系直接上传 dataURI ),得到 Blob 对象之后我们就可以把它 append 进FormData 对象里面了,如果我们需要上传该图片,我们就可以使用 Ajax 把 FormData 把图片 POST 到服务器端了。
HTMLCanvasElement.toDataURL() 方法返回一个包含图片展示的 data URI ,在指定图片格式为 image/jpeg 或 image/webp的情况下,可以从 0 到 1 的区间内选择图片的质量。如果超出取值范围,将会使用默认值 0.92。其他参数会被忽略。(关于toDataURL()方法的知识可以看看这里)
Blob对象表示不可变的类似文件对象的原始数据,它能存储着大量的二进制数据。(关于Blob对象的知识可以看看这里)
利用 FormData 对象,我们可以通过 JavaScript 用一些键值对来模拟一系列表单控件,我们还可以使用 XMLHttpRequest 的 send() 方法来异步的提交表单。与普通的 Ajax 相比,使用 FormData 的最大优点就是我们可以异步上传二进制文件。(关于FormData对象的知识可以看看这里)
不同压缩的图片质量之间的对比
也许我们会怀疑 HTMLCanvasElement.toDataURL() 方法对图片的压缩是否有效果,或者压缩的程度能不能满足我们的需求,我们可以看看两者之间的对比。
canvas.toDataURL(fileType, 1);
当图片质量(encoderOptions )赋值为1的时候,我们输出它:
这时图片是这样的:
canvas.toDataURL(fileType, 0.1);
然而,当图片质量赋值为0.1的时候,我们再次输出它:
这时的图片时这样的:
对比之后,我们可以看到图片的size明显变小,而且图片的清晰度明显下降了,所以通过HTMLCanvasElement.toDataURL() 方法的确能满足我们对图片进行压缩的要求。
因此,在实际应用中,我们可以根据我们的需求调整图片质量(encoderOptions )的值来实现对图片的压缩。