前端实现图片压缩上传

在文章中添加图片时,由于图片很大,导致浏览文章时严重影响用户体验,因此需要在上传是对图片进行压缩处理

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" name="viewport">
    <title>移动端图片压缩上传demo</title>
    <style>
        * {
            margin: 0;
            padding: 0;
        }

        li {
            list-style-type: none;
        }

        a,
        input {
            outline: none;
            -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
        }

        #choose {
            display: none;
        }

        canvas {
             100%;
            border: 1px solid #000000;
        }

        #upload {
            display: block;
            margin: 10px;
            height: 60px;
            text-align: center;
            line-height: 60px;
            border: 1px solid;
            border-radius: 5px;
            cursor: pointer;
        }

        .touch {
            background-color: #ddd;
        }

        .img-list {
            margin: 10px 5px;
        }

        .img-list li {
            position: relative;
            display: inline-block;
             100px;
            height: 100px;
            margin: 5px 5px 20px 5px;
            border: 1px solid rgb(100, 149, 198);
            background: #fff no-repeat center;
            background-size: cover;
        }

        .progress {
            position: absolute;
             100%;
            height: 20px;
            line-height: 20px;
            bottom: 0;
            left: 0;
            background-color: rgba(100, 149, 198, .5);
        }

        .progress span {
            display: block;
             0;
            height: 100%;
            background-color: rgb(100, 149, 198);
            text-align: center;
            color: #FFF;
            font-size: 13px;
        }

        .size {
            position: absolute;
             100%;
            height: 15px;
            line-height: 15px;
            bottom: -18px;
            text-align: center;
            font-size: 13px;
            color: #666;
        }

        .tips {
            display: block;
            text-align: center;
            font-size: 13px;
            margin: 10px;
            color: #999;
        }

        .pic-list {
            margin: 10px;
            line-height: 18px;
            font-size: 13px;
        }

        .pic-list a {
            display: block;
            margin: 10px 0;
        }

        .pic-list a img {
            vertical-align: middle;
            max- 30px;
            max-height: 30px;
            margin: -4px 0 0 10px;
        }
    </style>
</head>

<body>
    <input type="file" id="choose" accept="image/*" multiple>
    <ul class="img-list"></ul>
    <a id="upload">上传图片</a>
    <span class="tips">只允许上传jpg、png及gif</span>

    <script src="../../public/jquery-2.1.1.min.js"></script>
    <script>
        var filechooser = document.getElementById("choose");

        //    用于压缩图片的canvas
        var canvas = document.createElement("canvas");
        var ctx = canvas.getContext('2d');

        //    瓦片canvas
        var tCanvas = document.createElement("canvas");
        var tctx = tCanvas.getContext("2d");

        var maxsize = 300 * 1024; // 最大 300kb

        $("#upload").on("click", function () {
                filechooser.click();
            })
            .on("touchstart", function () {
                $(this).addClass("touch")
            })
            .on("touchend", function () {
                $(this).removeClass("touch")
            });

        filechooser.onchange = function () {
            if (!this.files.length) return;

            var files = Array.prototype.slice.call(this.files);

            if (files.length > 9) {
                alert("最多同时只可上传9张图片");
                return;
            }

            files.forEach(function (file, i) {
                if (!//(?:jpeg|png|gif)/i.test(file.type)) return;

                var reader = new FileReader();

                var li = document.createElement("li");

                // 获取原图大小
                var size = file.size / 1024 > 1024 ? (~~(10 * file.size / 1024 / 1024)) / 10 + "MB" : ~~(
                    file.size / 1024) + "KB";
                
                li.innerHTML = '<div class="progress"><span></span></div><div class="size">' + size +
                    '</div>';
                $(".img-list").append($(li));

                reader.onload = function () {
                    var result = this.result;
                    var img = new Image();
                    img.src = result;

                    $(li).css("background-image", "url(" + result + ")");

                    //如果图片大小小于300kb,则直接上传
                    if (result.length <= maxsize) {
                        img = null;

                        upload(result, file.type);

                        return;
                    }

                    // 图片加载完毕之后进行压缩,然后上传
                    if (img.complete) {
                        callback();
                    } else {
                        img.onload = callback;
                    }

                    function callback() {
                        var data = compress(img);
                        upload(data, file.type);
                        img = null;
                    }
                };

                reader.readAsDataURL(file);
            })
        };

        //    使用canvas对大图片进行压缩
        function compress(img) {
            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;
            }
            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);
            }

            // 动态计算压缩比,压缩到最接近 maxsize的状态
            const range = +(maxsize / initSize).toFixed(1)
            var ndata = canvas.toDataURL('image/jpeg', range);

            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;
        }

        //    图片上传,将base64的图片转成二进制对象,塞进formdata上传
        function upload(basestr, type) {
            console.log(basestr.length / 1024, type, dataURItoBlob(basestr))
        }

        function dataURItoBlob(dataURI) {
            var binary = atob(dataURI.split(',')[1]);
            var array = [];
            for (var i = 0; i < binary.length; i++) {
                array.push(binary.charCodeAt(i));
            }
            return new Blob([new Uint8Array(array)], {
                type: 'image/jpeg'
            });
        }
    </script>
</body>

</html>

服务器端代码

/*
 *移动端图片压缩上传功能后台
 */
"use strict";

var fs = require('fs');
var router = require("../router");
//var FormParser = require("./formParser");
var formidable = require('formidable');
var path = require('path');

var fileSaveDir = path.join(STATIC_PATH, 'upload');

router.setMap({
  "uindex_2": path.join(__dirname, "./index_2.html"),
  "cupload": cupload
});

function cupload(req, res) {
  if (!fs.existsSync(fileSaveDir)) {
    fs.mkdirSync(fileSaveDir)
  }

  var form = new formidable.IncomingForm();
  var responseData = [];
  form.uploadDir = fileSaveDir;
  form.type = true;
  form.keepExtensions = true;

  form.parse(req, function(err, fields, files){
    if(!err) {
      Object.keys(files).forEach(function(key){
        var file = files[key];
        var filename = path.basename(file.path);

        //每张图片给予一分钟保存时间
        setTimeout(function() {
          if (!fs.existsSync(file.path)) return;

          console.log("x1B[33m删除文件" + filename + "x1B[0m");
          fs.unlinkSync(file.path);
        }, 60 * 1000);

        // 塞入响应数据中
        responseData.push({
          type: file.type,
          name: filename,
          path: '/public/upload/' + filename,
          size: file.size / 1024 > 1024 ? (~~(10 * file.size / 1024 / 1024)) / 10 + "MB" : ~~(file.size / 1024) + "KB"
        });
      });
    } else {
      console.warn(err);
    }

    res.writeHead(200);
    res.end(JSON.stringify(responseData));
  });
}

代码出自 https://github.com/whxaxes/node-test, 略微做了调整

常用网站: SegmentFault | GitHub | 掘金社区
原文地址:https://www.cnblogs.com/yesyes/p/15382590.html