前端实现压缩图片上传

2017-10-23 · xiejiahe

其实就是利用canvas的 toDataURL,图片压缩不支持png会越压越大

图片压缩支持image/jpeg, Chrome支持image/webp

压缩前后端都能实现,这里说一下前端压缩

toDataURL语法

canvas.toDataURL(type, encoderOptions);

// type 可选,图片格式,默认是 image/png,

// encoderOptions 可选,图片质量 0 - 1,如果超出取值范围默认值0.92

toDataURL 用法例子

var base64 = canvasEl.toDataURL('image/jpeg', .5);
console.log(base64);

前端代码, 二进制上传,非base64

<!DOCTYPE html>
<html>
<head>
	<meta charset="UTF-8">
	<title>文件上传</title>
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<script src="//cdn.bootcss.com/axios/0.16.2/axios.min.js"></script>
</head>
<body>
<div id="app">
	<input type="file" id="file">
</div>

<script>
// base64 转换blob对象用于上传
function dataURItoBlob(dataurl) {
	var arr = dataurl.split(',');
	var mime = arr[0].match(/:(.*?);/)[1];
	var bstr = window.atob(arr[1]);
	var n = bstr.length;
	var u8arr = new Uint8Array(n);
	while(n--) u8arr[n] = bstr.charCodeAt(n);
	return new Blob([u8arr], {type: mime});
}

document.getElementById('file').onchange = function () {
	var file = this.files;
	if( file.length == 0 ) return;
	var fileReader = new FileReader();
	console.log(`压缩前字节大小:${file[0].size}`);
	fileReader.readAsDataURL(file[0]);
	fileReader.onload = function () {
		var img = new Image();
		img.src = this.result;
		img.onload = function () {
			// 创建canvas 实现压缩
			var canvas = document.createElement('canvas');
			var ctx = canvas.getContext('2d');
			canvas.width = img.naturalWidth;
			canvas.height = img.naturalHeight;
			ctx.drawImage(img, 0, 0);
			// 压缩类型, 压缩比例 0 - 1, 实测20MB压缩到 7MB多, 生成base64
			var url = canvas.toDataURL(file[0].type, .5);
			// base64 转换blob对象
			var blob = dataURItoBlob(url);
			console.log(`压缩后字节大小:${blob.size}`);
			// 创建 FormData 用于ajax上传
			var formData = new FormData();
			formData.append('file', blob);
			// Ajax发送给后端
			axios({
				method: 'POST',
				url: '/upload',
				data: formData,
			})
			.then(res => {
				console.log(res)
			})
			.catch(e => {
				console.log(e)
			})
		}
	}
}
</script>
</body>
</html>

Node.js接收

其实跟普通form表单接收是一样的

var express = require('express')
var app = express();
var formidable = require('formidable');
var fs = require('fs')
var path = require('path')
var crypto = require('crypto')
var md5 = str => {
    return crypto.createHash('md5').update(str.toString()).digest('hex')
}

app.use((req, res, next) => {
    // 不同源,设置跨域头
    res.set({
        'Access-Control-Allow-Origin': '*',
        'Access-Control-Allow-Methods': 'POST,GET',
        'Access-Control-Allow-Credentials': true
    })
    next();
})

app.set('views', path.join(__dirname, 'views'))
app.set('view engine', 'ejs')
app.get('/', (req, res) => {
    res.render('index')
})

// 这个才是主要的
app.post('/upload', (req, res) => {
    var form = new formidable.IncomingForm();
    form.uploadDir = './upload';
    form.parse(req, function (err, fields, files) {
        var suffix = {
            'image/jpeg': '.jpg',
            'image/png': '.png',
        }
        fs.renameSync(files.file.path, `./upload/${md5(Date.now())}${suffix[files.file.type]}`)
        res.json({
            files
        })
    });
})

app.listen(3000)
JavaScript
原创文章,转载请注明出处。