前端实现压缩图片上传

发表于:2017-10-23

其实就是利用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
广告