js前端js压缩图片代码实战jquery+canvas压缩图片
效果还是很让人满意,因为不仅仅是压缩那么简单,考虑到现在大多数手机随便拍张照片都有可能5-15M的,用户上传有的时候可不管你大小和服务器传输性能、存储性能,所以很难保障上传过程不出现什么问题,那么这个代码极大程度考虑兼容性,实测10M图片随便上传,尽可能保障分辨率的同时按照阶梯层度保障画质(500k-2M时画质优先,其它的太大了就画质一般,但好歹能随便上传且大小较低!)
结合参考资料,我加入了根据文件大小分配不同的压缩率,而不是写死每张图片压缩至10%
参考资料转载原文:
在IOS中,canvas绘制图片是有两个限制的:
首先是图片的大小,如果图片的大小超过两百万像素,图片也是无法绘制到canvas上的,调用drawImage的时候不会报错,但是你用toDataURL获取图片数据的时候获取到的是空的图片数据。
再者就是canvas的大小有限制,如果canvas的大小大于大概五百万像素(即宽高乘积)的时候,不仅图片画不出来,其他什么东西也都是画不出来的。
应对第一种限制,处理办法就是瓦片绘制了。瓦片绘制,也就是将图片分割成多块绘制到canvas上,我代码里的做法是把图片分割成100万像素一块的大小,再绘制到canvas上。
而应对第二种限制,我的处理办法是对图片的宽高进行适当压缩,我代码里为了保险起见,设的上限是四百万像素,如果图片大于四百万像素就压缩到小于四百万像素。四百万像素的图片应该够了,算起来宽高都有2000X2000了。
如此一来就解决了IOS上的两种限制了。
除了上面所述的限制,还有两个坑,一个就是canvas的toDataURL是只能压缩jpg的,当用户上传的图片是png的话,就需要转成jpg,也就是统一用canvas.toDataURL('image/jpeg', 0.1) , 类型统一设成jpeg,而压缩比就自己控制了。
另一个就是如果是png转jpg,绘制到canvas上的时候,canvas存在透明区域的话,当转成jpg的时候透明区域会变成黑色,因为canvas的透明像素默认为rgba(0,0,0,0),所以转成jpg就变成rgba(0,0,0,1)了,也就是透明背景会变成了黑色。解决办法就是绘制之前在canvas上铺一层白色的底色。
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<!--新增库-->
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<!--新增库-->
<title>图片上传</title>
<script language="JavaScript">
<!--
$(document).ready(function(){
$(":file").change(function(){
if (CheckInputFile(this)) {
$("#form1").hide();
//$("#form1").submit();
compressAndUploadImage('imgfile');
}
});
});
function CheckInputFile(file) {
var ExtIn = ["jpg", "gif", "bmp", "png"]; //允许后缀名
var ExtOut = []; //禁止后缀名
//检测变量
var bCheck = true;
//空值、文件数限制、后缀名
if (!$(file).val()) {
bCheck = false; alert("请选择一个文件");
} else if (!!ExtIn.length && !RegExp("\.(" + ExtIn.join("|") + ")$", "i").test($(file).val())) {
//检测是否允许后缀名
bCheck = false; alert("只允许上传" + ExtIn.join(",") + "文件");
} else if (!!ExtOut.length && RegExp("\.(" + ExtOut.join("|") + ")$", "i").test($(file).val())) {
//检测是否禁止后缀名
bCheck = false; alert("只允许上传" + ExtIn.join(",") + "文件");
}
return bCheck;
}
//-->
</script>
</head>
<body>
<form id="form1" name="form1" method="post" action="upfile.asp" enctype="multipart/form-data" >
<input type="hidden" name="uptype" value="<%=uptype%>" />
<a class="files" href="javascript:void(0);"><input type="file" id="imgfile" name="file1" class="upimginput" value="" /></a>
</form>
<div id="upLoading">文件上传中...</div>
<script>
function compressAndUploadImage(fileInputId) {
var fileInput = document.getElementById(fileInputId);
var file = fileInput.files[0];
if (!file.type.match(/image.*/)) {
alert('请选择一个图片文件!');
return;
}
var reader = new FileReader();
reader.onload = function (readerEvent) {
var image = new Image();
image.onload = function (imageEvent) {
if (image.width <= 1000 && image.height <= 1000 && file.size <= 500 * 1024) {
uploadFile(file); // 直接上传原文件
} else {
//关键压缩代码开始
var initSize = image.src.length;
var width = image.width;
var height = image.height;
// 如果图片大于四百万像素,计算压缩比并将大小压至400万以下
var ratio;
if ((ratio = width * height / 4000000) > 1) {
ratio = Math.sqrt(ratio);
width /= ratio;
height /= ratio;
} else {
ratio = 1;
}
// 如果宽度或高度大于800px,等比缩放到800以下
//可选,这样确保图片尺寸不会特别大,因为网页上用不着特别大
/*
if (width > 800 || height > 800) {
var maxDim = Math.max(width, height);
var scale = 800 / maxDim;
width *= scale;
height *= scale;
}
*/
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
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);
var tCanvas = document.createElement('canvas');
var tctx = tCanvas.getContext('2d');
tCanvas.width = nw;
tCanvas.height = nh;
for (var i = 0; i < count; i++) {
for (var j = 0; j < count; j++) {
tctx.clearRect(0, 0, tCanvas.width, tCanvas.height); // 清空临时画布
tctx.drawImage(image, 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(image, 0, 0, width, height); // 直接绘制小图
}
//根据文件大小计算压缩质量,单位K
var quality;
var fileSizeInK = file.size / 1024;
if (fileSizeInK <= 500) {
quality = 0.9;//压缩后是原本质量的90%
} else if (fileSizeInK > 500 && fileSizeInK <= 1000) {
quality = 0.8;
} else if (fileSizeInK > 1000 && fileSizeInK <= 1500) {
quality = 0.7;
} else if (fileSizeInK > 1500 && fileSizeInK <= 2000) {
quality = 0.6;
} else if (fileSizeInK > 2000 && fileSizeInK <= 2500) {
quality = 0.5;
} else if (fileSizeInK > 2500 && fileSizeInK <= 3000) {
quality = 0.4;
} else if (fileSizeInK > 3000 && fileSizeInK <= 3500) {
quality = 0.3;
} else if (fileSizeInK > 3500 && fileSizeInK <= 4000) {
quality = 0.2;
} else {
quality = 0.1;//压缩后是原本质量的10%
}
// 进行最小压缩
new Promise(function(resolve, reject) {
canvas.toBlob(function(blob) {
if (blob) {
resolve(blob);
} else {
reject(new Error("Canvas toBlob failed"));
}
}, 'image/jpeg', quality); // 设置为 jpeg 格式,质量 定义变量quality,单位0.1-1.0
})
.then(function(blob) {
if (blob.size <= 500 * 1024) { // 判断文件大小是否小于等于 500KB
uploadFile(blob); // 上传压缩文件
console.log('压缩前:' + initSize);
console.log('压缩后:' + blob.size);
console.log('压缩率:' + ~~(100 * (initSize - blob.size) / initSize) + "%");
} else {
alert('无法将图片压缩到小于500KB。');
}
})
.catch(function(error) {
console.error('压缩过程中出现错误:', error);
alert('图片压缩失败,请稍后重试。');
});
//关键压缩代码结束
}
};
image.src = readerEvent.target.result;
};
reader.readAsDataURL(file);
}
function uploadFile(blobOrFile) {
var formData = new FormData();
formData.append('file1', blobOrFile, 'image.jpg'); // 第三个参数是文件名
formData.append('uptype', document.querySelector('input[name="uptype"]').value);
$.ajax({
url: 'upfile.asp', // 上传脚本地址
type: 'POST',
data: formData,
processData: false, // 告诉jQuery不要处理发送的数据
contentType: false, // 告诉jQuery不要设置内容类型
success: function (response) {
console.log('文件上传成功');
// 创建一个DOM解析器
var parser = new DOMParser();
// 将返回的HTML字符串解析成一个文档对象
var doc = parser.parseFromString(response, 'text/html');
// 获取所有的script标签
var scripts = doc.querySelectorAll('script[type="text/javascript"]');
// 逐个执行script标签中的代码
scripts.forEach(function(script) {
eval(script.textContent);
});
},
error: function (jqXHR, textStatus, errorThrown) {
console.error('文件上传失败', textStatus, errorThrown);
}
});
}
</script>
</body>
</html>
- 1、压缩起到关键作用的是//关键压缩代码开始...//关键压缩代码结束
- 2、具体根据项目实际进行修改,表单、字段、输入框名称
参考资料:
https://www.cnblogs.com/moqiutao/p/6279905.html
https://blog.51cto.com/u_12205/6830173