现在网页的图片交互越来越多,对于图片的上传、预览、压缩、裁剪等处理,都很常见,自己也查了很多资料,写了一个很常见的图片上传实例,自己的项目中也经常使用,现在总结一下相关的技术,也供后面参考和改进。具体实现效果如下
相关内容
1.图片预览
通常上传文件使用input表单来,就是只能显示文件名,现在基本都是通过图片预览,让用户更好的操作。实现预览原理就是将input获取到的文件通过createObjectURL转化成img的src路径,实现图片的展示
<input type="file" accept="image/*" id="inputfile" @change="preImg('inputfile')" >
getFileUrl:function (id) {//根据输入框id 生成图片src
console.log(document.getElementById(id).files[0]);
var url;
if (navigator.userAgent.indexOf("MSIE")>=1) { // IE
url = document.getElementById("inputfile").value;
} else if(navigator.userAgent.indexOf("Firefox")>0) { // Firefox
url = window.URL.createObjectURL(document.getElementById(id).files.item(0));
} else if(navigator.userAgent.indexOf("Chrome")>0) { // Chrome
url = window.URL.createObjectURL(document.getElementById(id).files.item(0));
}else {
url = window.webkitURL.createObjectURL(document.getElementById(id).files[0]);
}
return url;
},
2.图片的上传
和文件上传一样,需要通过formdata进行上传,jquery的ajax上传文件还需要设置mimeType,还需要取消type:'json',因此成功后的内容为字符串格式,需要重新转化成json,里面的subFile是一个存放文件的数组,每个值是input的files值==>document.getElementById(id).files.item(0)或者document.getElementById(id).files[0],根据不同浏览器不同,见上图。
var formData = new FormData();
for(var k in this.subFile){ //文件数组
formData.append('pPath',this.subFile[k]);
}
formData.append('type',this.addType);
formData.append('message',this.addContent);
formData.append('userNo',JSON.parse(localStorage.getItem('skyUser')).userNo);
formData.append('token',localStorage.getItem('skyUsertoken'));
$.ajax({
type:"post",
url:vm.path+"makeStatement.json",
data:formData,
cache: false,
contentType: false,
processData:false,
mimeType:"multipart/form-data",
success:function(data){
var a=JSON.parse(data);
}});
3.图片压缩
基于现在手机拍照,照片都很大,严重影响上传速度,因此需要对图片进行压缩上传,具体也是基于canvas的画图canvas.toDataURL('image/jpeg', quality);下面方法就是通过img的src进行图片压缩,obj设置压缩后宽高,callback函数返回图片的base64值。
dealImage: function (path, obj, callback) {
var img = new Image();
img.src = path;
img.onload = function () {
var that = this;
// 默认按比例压缩
var w = that.width,
h = that.height,
scale = w / h;
w = obj.width || w;
h = obj.height || (w / scale);
var quality = 0.9;
//生成canvas
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
// 创建属性节点
var anw = document.createAttribute("width");
anw.nodeValue = w;
var anh = document.createAttribute("height");
anh.nodeValue = h;
canvas.setAttributeNode(anw);
canvas.setAttributeNode(anh);
ctx.drawImage(that, 0, 0, w, h);
// 图像质量
if (obj.quality && obj.quality <= 1 && obj.quality > 0) {
quality = obj.quality;
}
// quality值越小,所绘制出的图像越模糊
var base64 = canvas.toDataURL('image/jpeg', quality);
// 回调函数返回base64的值
callback(base64);
}
},
压缩后的图片为base64则需要转化成blob对象才能进行上传,具体实现如下方法
convertBase64UrlToBlob: function (urlData) {
var bytes = window.atob(urlData.split(',')[1]); //去掉url的头,并转换为byte
//处理异常,将ascii码小于0的转换为大于0
var ab = new ArrayBuffer(bytes.length);
var ia = new Uint8Array(ab);
for (var i = 0; i < bytes.length; i++) {
ia[i] = bytes.charCodeAt(i);
}
return new Blob([ab], {type: 'image/png'});
},
因此在压缩的callback里调用
let vm = this;
vm.dealImage(src,{width:300},function (base) {
console.log(vm.convertBase64UrlToBlob(base));
vm.subFile.push(vm.convertBase64UrlToBlob(base))
console.log(vm.imgs);
})
完整代码如下,复制出来直接使用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<meta content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no" name="viewport">
<meta content="yes" name="apple-mobile-web-app-capable">
<meta content="black" name="apple-mobile-web-app-status-bar-style">
</head>
<style>
.addItem{
width: 100%;
height: 100%;
position: fixed;
top: 0;
z-index: 9999;
font-size: 0.3rem;
background-color: #fff;
overflow: scroll;
}
.addItem .add_content{
border-bottom: dashed #fda7f7 1px;
}
.cream {
position: relative;
display: inline-block;
overflow: hidden;
text-decoration: none;
text-indent: 0;
margin-top: 10px;
margin-left: 10px;
width: 80px;
height: 80px;
border-radius: 5px;
border: solid 1px #eee;
}
.cream .addimg{
font-size: 30px;
color: #555;
text-align: center;
line-height: 80px;
}
.cream input {
position: absolute;
display: block;
font-size: 60px;
right: 0;
top: 0;
opacity: 0;
}
.cream:hover {
text-decoration: none;
}
.deleteImg{
position: absolute;
right: 2px;
top: 2px;
width: 20px;
height: 20px;
background: rgba(150,150,150,0.5);
text-align: center;
line-height: 20px;
font-size: 20px;
color: #fff;
}
</style>
<body>
<div id="main">
<div id="smallimages">
<div class="cream" v-for="(img,n) in imgs" :key="n">
<div class="deleteImg" @click="deleteImg(n)">-</div>
<img :src="img" width="80px" height="80px">
</div>
<div class="cream" v-show="cream">
<div class="addimg">+</div>
<input type="file" accept="image/*" id="inputfile" @change="preImg('inputfile')" >
</div>
</div>
</div>
</body>
<script src="http://code.jquery.com/jquery-1.4.1.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
<script>
var vm = new Vue({
el:"#main",
data:function() {
return {
addType: '说说',
cream:true,
imgs:[],//显示的图片
subFile:[],//上传的图片
}
},
watch:{//设置最多上传9张
imgs:function (val,old) {
if(val.length>=9){
this.cream = false
}else {
this.cream = true
}
}
},
methods:{
preImg:function (id) {
var vm = this;
var src = this.getFileUrl(id);
vm.imgs.push(src);
console.log( this.getFileUrl(id));
vm.dealImage(src,{width:300},function (base) {
console.log(vm.convertBase64UrlToBlob(base));
vm.subFile.push(vm.convertBase64UrlToBlob(base))
console.log(vm.imgs);
})
},
getFileUrl:function (id) {//根据输入框id 生成图片src
console.log(document.getElementById(id).files[0]);
var url;
if (navigator.userAgent.indexOf("MSIE")>=1) { // IE
url = document.getElementById("inputfile").value;
} else if(navigator.userAgent.indexOf("Firefox")>0) { // Firefox
url = window.URL.createObjectURL(document.getElementById(id).files.item(0));
} else if(navigator.userAgent.indexOf("Chrome")>0) { // Chrome
url = window.URL.createObjectURL(document.getElementById(id).files.item(0));
}else {
url = window.webkitURL.createObjectURL(document.getElementById(id).files[0]);
}
return url;
},
dealImage: function (path, obj, callback) {//进行图片压缩
var img = new Image();
img.src = path;
img.onload = function () {
var that = this;
// 默认按比例压缩
var w = that.width,
h = that.height,
scale = w / h;
w = obj.width || w;
h = obj.height || (w / scale);
var quality = 0.9;
//生成canvas
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
// 创建属性节点
var anw = document.createAttribute("width");
anw.nodeValue = w;
var anh = document.createAttribute("height");
anh.nodeValue = h;
canvas.setAttributeNode(anw);
canvas.setAttributeNode(anh);
ctx.drawImage(that, 0, 0, w, h);
// 图像质量
if (obj.quality && obj.quality <= 1 && obj.quality > 0) {
quality = obj.quality;
}
// quality值越小,所绘制出的图像越模糊
var base64 = canvas.toDataURL('image/jpeg', quality);
// 回调函数返回base64的值
callback(base64);
}
},
convertBase64UrlToBlob: function (urlData) {
var bytes = window.atob(urlData.split(',')[1]); //去掉url的头,并转换为byte
//处理异常,将ascii码小于0的转换为大于0
var ab = new ArrayBuffer(bytes.length);
var ia = new Uint8Array(ab);
for (var i = 0; i < bytes.length; i++) {
ia[i] = bytes.charCodeAt(i);
}
return new Blob([ab], {type: 'image/png'});
},
deleteImg:function (n) {
console.log(n);
this.imgs.splice(n,1);
this.subFile.splice(n,1);
console.log(this.imgs)
console.log(this.subFile)
},
subm:function () {
var vm = this;
var formData = new FormData();
for(var k in this.subFile){ //文件数组
formData.append('pPath',this.subFile[k]);
}
formData.append('type',this.addType);
$.ajax({
type:"post",
url:"",
data:formData,
cache: false,
contentType: false,
processData:false,
mimeType:"multipart/form-data",
success:function(data){
var a=JSON.parse(data);
console.log(a);
}});
}
},
})
</script>
</html>
通过控制台可以打印出压缩前后的图片大小
最后分享一个基于vue的图片上传插件 vue-corppa 官网链接:https://zhanziyang.github.io/vue-croppa/#/quick-start很是好用。功能齐全,包括图片的压缩,裁剪,形状裁剪等