asp.net mvc 中前端多张图片压缩上传功能实现
由于网站需要上传多张图片,图片原始大小太大了,都是几M的,同时解决了图片在手机端旋转问题和对大分辨的图片进行等比例缩小
前台代码:
@{ Layout = "~/Views/Shared/_MyLayoutPage.cshtml"; } <!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width" /> <script src="~/Scripts/jquery-3.3.1.min.js"></script> <link href="~/Content/bootstrap.min.css" rel="stylesheet" /> <script src="~/Scripts/bootstrap.min.js"></script> <link href="~/Scripts/tinyImgUpload/css/tinyImgUpload.css" rel="stylesheet" /> <script src="~/Scripts/tinyImgUpload/js/tinyImgUpload.js"></script> <title>UpLoadPicPage</title> <script> function uploadImg() { var eleList = document.querySelectorAll("#upload"); var ele = eleList[0].querySelector('#img-container'); var formData = new FormData(); for (var i = 0, f; f = ele.files[i]; i++) { formData.append('files'+i, f); } $.ajax({ url: "/PicManager/AddPicPage", type: "post", async: false, contentType: false, processData: false, data: formData, success: function (data) { if (data == "ok") { window.location.href = "@Url.Action("UserLoginPage", "Login")"; } else { alert("上传图片失败"); } }, error: function (data) { } }); } </script> </head> <body> <div class="container" style="margin-top:70px;"> <form id="formtable" enctype="multipart/form-data" method="post"> <div id="upload"></div> <a style=" cursor:pointer; border-radius:5px; margin-top:10px; width:60px; display:block; height:30px; text-align:center;line-height:30px; background-color:#00CCFF;border:0;letter-spacing:10px; color:#fff;" class="submit">上传</a> </form> </div> <script> var upload = tinyImgUpload('#upload', 1); document.getElementsByClassName('submit')[0].onclick = function (e) { uploadImg(); } </script> </body> </html>
tinyImgUpload.css
body { font-size:14px; } #img-container { } #img-container:after { content: '.'; display: block; height: 0; visibility: hidden; overflow: hidden; clear: both; } .img-item { position: relative; float: left; margin-right: 0.1875rem; margin-bottom: 0.1875rem; height: 7rem; width: 7rem; box-sizing: border-box; } .img-thumb { border: 1px solid #000; margin-right: 7px; } .thumb-icon { width: 100%; height: 100%; } .img-up-add { display: table; border: 1px dashed #E0E0E0; } .img-add-icon { display: table-cell; vertical-align: middle; text-align: center; line-height:normal; } .img-remove { position: absolute; right: -0.1875rem; top: -0.1875rem; display: block; width: 2rem; height: 2rem; border-radius: 50%; background: #f7333d; color: #fff; text-align: center; text-decoration: none; font-size: 0.25rem; overflow: hidden; background-clip: padding-box; } #img-file-input { display: none; }
tinyImgUpload.js
/**
* tinyImgUpload
* @param ele [string] [生成组件的元素的选择器]
* @param options [Object] [对组件设置的基本参数]
* options具体参数如下
* path 图片上传的地址路径 必需
* onSuccess(res) 文件上传成功后的回调 参数为返回的文本 必需
* onFailure(res) 文件上传失败后的回调 参数为返回的文本 必需
* @return [function] [执行图片上传的函数]
* 调用方法
* tinyImgUpload('div', options)
*/
function tinyImgUpload(ele, options) {
// 用于压缩图片的canvas
var canvas = document.createElement("canvas");
var ctx = canvas.getContext('2d');
// 瓦片canvas
var tCanvas = document.createElement("canvas");
var tctx = tCanvas.getContext("2d");
var maxsize = 100 * 1024;
// 判断容器元素合理性并且添加基础元素
var eleList = document.querySelectorAll(ele);
if (eleList.length == 0) {
console.log('绑定的元素不存在');
return;
} else if (eleList.length > 1) {
console.log('请绑定唯一元素');
return;
} else {
eleList[0].innerHTML = '<div id="img-container" >' +
'<div class="img-up-add img-item"> <span class="img-add-icon" style="font-size:6rem;">+</span> </div>' +
'<input type="file" name="files" id="img-file-input" multiple>' +
'</div>';
var ele = eleList[0].querySelector('#img-container');
ele.files = []; // 当前上传的文件数组
}
// 为添加按钮绑定点击事件,设置选择图片的功能
var addBtn = document.querySelector('.img-up-add');
addBtn.addEventListener('click', function () {
document.querySelector('#img-file-input').value = null;
document.querySelector('#img-file-input').click();
return false;
}, false)
// 使用canvas对大图片进行压缩
function compress(img, Orientation) {
var maxW = 1080;//设置最大宽度
var maxH = 1080;
var initSize = img.src.length;
var width = img.width;//图片宽度
var height = img.height;//图片高度
//如果图片大于四百万像素,计算压缩比并将大小压至400万以下
var ratio;
if ((ratio = width * height / 4000000) > 1) {
ratio = Math.sqrt(ratio);
width /= ratio;
height /= ratio;
} else {
ratio = 1;
}
if (img.width < maxW && img.height < maxH) {
width = img.width;//图片宽度
height = img.height;//图片高度
}
else //原图片宽高比例 大于 图片框宽高比例,则以框的宽为标准缩放,反之以框的高为标准缩放
{
if (maxW / maxH <= img.width / img.height) //原图片宽高比例 大于 图片框宽高比例
{
width = maxW; //以框的宽度为标准
height = maxW * (img.height / img.width);
}
else { //原图片宽高比例 小于 图片框宽高比例
width = maxH * (img.width / img.height);
height = maxH; //以框的高度为标准
}
}
canvas.width = width;
canvas.height = height;
// 铺底色
ctx.fillStyle = "#fff";
ctx.fillRect(0, 0, canvas.width, canvas.height);
//如果图片像素大于100万则使用瓦片绘制
var count;
if ((count = width * height / 1000000) > 1) {
count = ~~(Math.sqrt(count) + 1); //计算要分成多少块瓦片
// 计算每块瓦片的宽和高
var nw = ~~(width / count);
var nh = ~~(height / count);
tCanvas.width = nw;
tCanvas.height = nh;
for (var i = 0; i < count; i++) {
for (var j = 0; j < count; j++) {
tctx.drawImage(img, i * nw * ratio, j * nh * ratio, nw * ratio, nh * ratio, 0, 0, nw, nh);
ctx.drawImage(tCanvas, i * nw, j * nh, nw, nh);
}
}
} else {
ctx.drawImage(img, 0, 0, width, height);
}
//修复ios上传图片的时候 被旋转的问题
if (Orientation != "" && Orientation != 1) {
switch (Orientation) {
case 6://需要顺时针(向左)90度旋转
rotateImg(img, 'left', canvas);
break;
case 8://需要逆时针(向右)90度旋转
rotateImg(img, 'right', canvas);
break;
case 3://需要180度旋转
rotateImg(img, 'right', canvas);//转两次
rotateImg(img, 'right', canvas);
break;
}
}
//进行最小压缩
var ndata = canvas.toDataURL('image/jpeg', 0.5);
console.log('压缩前:' + initSize);
console.log('压缩后:' + ndata.length);
console.log('压缩率:' + ~~(100 * (initSize - ndata.length) / initSize) + "%");
tCanvas.width = tCanvas.height = canvas.width = canvas.height = 0;
return ndata;
}
function rotateImg(img, direction, canvas) {
//最小与最大旋转方向,图片旋转4次后回到原方向
const min_step = 0;
const max_step = 3;
if (img == null) return;
//img的高度和宽度不能在img元素隐藏后获取,否则会出错
let height = canvas.height;
let width = canvas.width;
let step = 2;
if (step == null) {
step = min_step;
}
if (direction == 'right') {
step++;
//旋转到原位置,即超过最大值
step > max_step && (step = min_step);
} else {
step--;
step < min_step && (step = max_step);
}
//旋转角度以弧度值为参数
let degree = step * 90 * Math.PI / 180;
let ctx = canvas.getContext('2d');
switch (step) {
case 0:
canvas.width = width;
canvas.height = height;
ctx.drawImage(img, 0, 0, width, height);
break;
case 1:
canvas.width = height;
canvas.height = width;
ctx.rotate(degree);
ctx.drawImage(img, 0, -height, width, height);
break;
case 2:
canvas.width = width;
canvas.height = height;
ctx.rotate(degree);
ctx.drawImage(img, -width, -height, width, height);
break;
case 3:
canvas.width = height;
canvas.height = width;
ctx.rotate(degree);
ctx.drawImage(img, -width, 0, width, height);
break;
}
}
// 预览图片
//处理input选择的图片
function handleFileSelect(evt) {
let Orientation;
var files = evt.target.files;
for (var i = 0, f; f = files[i]; i++) {
// 过滤掉非图片类型文件
if (!f.type.match('image.*')) {
continue;
}
if (files.length > 9) {
alert("最多同时只可上传9张图片");
return;
}
// 过滤掉重复上传的图片
var tip = false;
for (var j = 0; j < (ele.files).length; j++) {
if ((ele.files)[j].name == f.name) {
tip = true;
break;
}
}
//去获取拍照时的信息,解决拍出来的照片旋转问题
EXIF.getData(f, function () {
Orientation = EXIF.getTag(this, 'Orientation');
});
if (!tip) {
// 图片文件绑定到容器元素上
var reader = new FileReader();
reader.onload = (function (theFile) {
var FfileName = theFile.name;//传文件名用的
return function (e) {
console.log(FfileName);
var result = e.target.result;
var img = new Image();
img.src = result;
//如果图片大小小于100kb,则直接上传
//if (result.length <= maxsize) {
// ele.files.push(theFile);
// img = null;
// var oDiv = document.createElement('div');
// oDiv.className = 'img-thumb img-item';
// // 向图片容器里添加元素
// oDiv.innerHTML = '<img class="thumb-icon" src="' + result + '" />' +
// '<a href="javscript:;" class="img-remove">x</a>'
// ele.insertBefore(oDiv, addBtn);
// return;
//} else {
// 图片加载完毕之后进行压缩,然后上传
if (img.complete) {
callback();
} else {
img.onload = callback;
}
// }
function callback() {
var data = compress(img, Orientation);
var oDiv = document.createElement('div');
oDiv.className = 'img-thumb img-item';
// 向图片容器里添加元素
oDiv.innerHTML = '<img class="thumb-icon" src="' + data + '" />' +
'<a href="javscript:;" class="img-remove">x</a>'
ele.insertBefore(oDiv, addBtn);
//将base64转换为file文件
function dataURLtoFile(dataurl, filename) {
var arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1],
bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
return new File([u8arr], filename, { type: mime });
}
var base64Img = data // base64编码的图片
var imgFile = dataURLtoFile(base64Img, FfileName)
ele.files.push(imgFile);
img = null;
}
};
})(f);
reader.readAsDataURL(f);
}
}
}
document.querySelector('#img-file-input').addEventListener('change', handleFileSelect, false);
// 删除图片
function removeImg(evt) {
if (evt.target.className.match(/img-remove/)) {
console.log('3', ele.files);
// 获取删除的节点的索引
function getIndex(ele) {
if (ele && ele.nodeType && ele.nodeType == 1) {
var oParent = ele.parentNode;
var oChilds = oParent.children;
for (var i = 0; i < oChilds.length; i++) {
if (oChilds[i] == ele)
return i;
}
} else {
return -1;
}
}
// 根据索引删除指定的文件对象
var index = getIndex(evt.target.parentNode);
ele.removeChild(evt.target.parentNode);
if (index < 0) {
return;
} else {
ele.files.splice(index, 1);
}
console.log('4', ele.files);
}
}
ele.addEventListener('click', removeImg, false);
// 上传图片
function uploadImg() {
console.log(ele.files);
var xhr = new XMLHttpRequest();
var formData = new FormData();
for (var i = 0, f; f = ele.files[i]; i++) {
formData.append('files', f);
}
console.log('1', ele.files);
console.log('2', formData);
}
return uploadImg;
}
效果图: