zoukankan      html  css  js  c++  java
  • Html5+asp.net mvc 图片压缩上传

    在做图片上传时,大图片如果没有压缩直接上传时间会非常长,因为有的图片太大,传到服务器上再压缩太慢了,而且损耗流量。

    思路是将图片抽样显示在canvas上,然后用通过canvas.toDataURL方法得到base64字符串来实现压缩。

    废话不多少不多说直接看代码:

    本次测试使用了 zepto.min.js 插件,更新版本的下载请点击这里

    主要js代码:

    //图片压缩处理
    ; (function () {
        /**
         * 加载的时候进行抽样检测
         * 在iOS中,大于2M的图片会抽样渲染
         */
        function detectSubsampling(img) {
            var iw = img.naturalWidth, ih = img.naturalHeight;
            if (iw * ih > 1024 * 1024) { //大于百万像素的就会进行抽样
                var canvas = document.createElement('canvas');
                canvas.width = canvas.height = 1;
                var ctx = canvas.getContext('2d');
                ctx.drawImage(img, -iw + 1, 0);
                // subsampled image becomes half smaller in rendering size.
                // check alpha channel value to confirm image is covering edge pixel or not.
                // if alpha value is 0 image is not covering, hence subsampled.
                return ctx.getImageData(0, 0, 1, 1).data[3] === 0;
            } else {
                return false;
            }
        }
    
        /**
         * Detecting vertical squash in loaded image.
         * Fixes a bug which squash image vertically while drawing into canvas for some images.
         */
        function detectVerticalSquash(img, iw, ih) {
            var canvas = document.createElement('canvas');
            canvas.width = 1;
            canvas.height = ih;
            var ctx = canvas.getContext('2d');
            ctx.drawImage(img, 0, 0);
            var data = ctx.getImageData(0, 0, 1, ih).data;
            // search image edge pixel position in case it is squashed vertically.
            var sy = 0;
            var ey = ih;
            var py = ih;
            while (py > sy) {
                var alpha = data[(py - 1) * 4 + 3];
                if (alpha === 0) {
                    ey = py;
                } else {
                    sy = py;
                }
                py = (ey + sy) >> 1;
            }
            var ratio = (py / ih);
            return (ratio === 0) ? 1 : ratio;
        }
    
        /**
         * Rendering image element (with resizing) and get its data URL
         */
        function renderImageToDataURL(img, options, doSquash) {
            var canvas = document.createElement('canvas');
            renderImageToCanvas(img, canvas, options, doSquash);
            return canvas.toDataURL("image/jpeg", options.quality || 0.8);
        }
    
        /**
         * Rendering image element (with resizing) into the canvas element
         */
        function renderImageToCanvas(img, canvas, options, doSquash) {
            var iw = img.naturalWidth, ih = img.naturalHeight;
            if (!(iw + ih)) return;
            var width = options.width, height = options.height;
            var ctx = canvas.getContext('2d');
            ctx.save();
            transformCoordinate(canvas, ctx, width, height, options.orientation);
            var subsampled = detectSubsampling(img);
            if (subsampled) {
                iw /= 2;
                ih /= 2;
            }
            var d = 1024; // size of tiling canvas
            var tmpCanvas = document.createElement('canvas');
            tmpCanvas.width = tmpCanvas.height = d;
            var tmpCtx = tmpCanvas.getContext('2d');
            var vertSquashRatio = doSquash ? detectVerticalSquash(img, iw, ih) : 1;
            var dw = Math.ceil(d * width / iw);
            var dh = Math.ceil(d * height / ih / vertSquashRatio);
            var sy = 0;
            var dy = 0;
            while (sy < ih) {
                var sx = 0;
                var dx = 0;
                while (sx < iw) {
                    tmpCtx.clearRect(0, 0, d, d);
                    tmpCtx.drawImage(img, -sx, -sy);
                    ctx.drawImage(tmpCanvas, 0, 0, d, d, dx, dy, dw, dh);
                    sx += d;
                    dx += dw;
                }
                sy += d;
                dy += dh;
            }
            ctx.restore();
            tmpCanvas = tmpCtx = null;
        }
    
        /**
         * Transform canvas coordination according to specified frame size and orientation
         * 根据帧的大小和方向调整canvas
         * 方向值是来自EXIF标签
         */
        function transformCoordinate(canvas, ctx, width, height, orientation) {
            switch (orientation) {
                case 5:
                case 6:
                case 7:
                case 8:
                    canvas.width = height;
                    canvas.height = width;
                    break;
                default:
                    canvas.width = width;
                    canvas.height = height;
            }
            switch (orientation) {
                case 2:
                    // horizontal flip
                    ctx.translate(width, 0);
                    ctx.scale(-1, 1);
                    break;
                case 3:
                    // 180 rotate left
                    ctx.translate(width, height);
                    ctx.rotate(Math.PI);
                    break;
                case 4:
                    // vertical flip
                    ctx.translate(0, height);
                    ctx.scale(1, -1);
                    break;
                case 5:
                    // vertical flip + 90 rotate right
                    ctx.rotate(0.5 * Math.PI);
                    ctx.scale(1, -1);
                    break;
                case 6:
                    // 90 rotate right
                    ctx.rotate(0.5 * Math.PI);
                    ctx.translate(0, -height);
                    break;
                case 7:
                    // horizontal flip + 90 rotate right
                    ctx.rotate(0.5 * Math.PI);
                    ctx.translate(width, -height);
                    ctx.scale(-1, 1);
                    break;
                case 8:
                    // 90 rotate left
                    ctx.rotate(-0.5 * Math.PI);
                    ctx.translate(-width, 0);
                    break;
                default:
                    break;
            }
        }
    
        var URL = window.URL && window.URL.createObjectURL ? window.URL :
                  window.webkitURL && window.webkitURL.createObjectURL ? window.webkitURL :
                  null;
    
        /**
         * MegaPixImage class
         */
        function MegaPixImage(srcImage) {
            if (window.Blob && srcImage instanceof Blob) {
                if (!URL) { throw Error("No createObjectURL function found to create blob url"); }
                var img = new Image();
                img.src = URL.createObjectURL(srcImage);
                this.blob = srcImage;
                srcImage = img;
            }
            if (!srcImage.naturalWidth && !srcImage.naturalHeight) {
                var _this = this;
                srcImage.onload = srcImage.onerror = function () {
                    var listeners = _this.imageLoadListeners;
                    if (listeners) {
                        _this.imageLoadListeners = null;
                        for (var i = 0, len = listeners.length; i < len; i++) {
                            listeners[i]();
                        }
                    }
                };
                this.imageLoadListeners = [];
            }
            this.srcImage = srcImage;
        }
    
        /**
         * Rendering megapix image into specified target element
         */
        MegaPixImage.prototype.render = function (target, options, callback) {
            if (this.imageLoadListeners) {
                var _this = this;
                this.imageLoadListeners.push(function () { _this.render(target, options, callback); });
                return;
            }
            options = options || {};
            var imgWidth = this.srcImage.naturalWidth, imgHeight = this.srcImage.naturalHeight,
                width = options.width, height = options.height,
                maxWidth = options.maxWidth, maxHeight = options.maxHeight,
                doSquash = !this.blob || this.blob.type === 'image/jpeg';
            if (width && !height) {
                height = (imgHeight * width / imgWidth) << 0;
            } else if (height && !width) {
                width = (imgWidth * height / imgHeight) << 0;
            } else {
                width = imgWidth;
                height = imgHeight;
            }
            if (maxWidth && width > maxWidth) {
                width = maxWidth;
                height = (imgHeight * width / imgWidth) << 0;
            }
            if (maxHeight && height > maxHeight) {
                height = maxHeight;
                width = (imgWidth * height / imgHeight) << 0;
            }
            var opt = {  width, height: height };
            for (var k in options) opt[k] = options[k];
            target.tagName = target.tagName || "IMG";
            var tagName = target.tagName.toLowerCase();
            if (tagName === 'img') {
                target.src = renderImageToDataURL(this.srcImage, opt, doSquash);
            } else if (tagName === 'canvas') {
                renderImageToCanvas(this.srcImage, target, opt, doSquash);
            }
            if (typeof this.onrender === 'function') {
                this.onrender(target);
            }
            if (callback) {
                callback();
            }
            if (this.blob) {
                this.blob = null;
                URL.revokeObjectURL(this.srcImage.src);
            }
        };
    
        /**
         * Export class to global
         */
        if (typeof define === 'function' && define.amd) {
            define([], function () { return MegaPixImage; }); // for AMD loader
        } else if (typeof exports === 'object') {
            module.exports = MegaPixImage; // for CommonJS
        } else {
            this.MegaPixImage = MegaPixImage;
        }
    
    })();
    
    ; (function ($) {
        $.extend($, {
            fileUpload: function (options) {
                var para = {
                    multiple: true, //是否可以多选上传
                    filebutton: ".filePicker", //上传文件的按钮
                    uploadButton: null,
                    url: "/Home/NotCompressUploadImg", //小型图片不压缩上传url
                    filebase: "mfile", // 此参数要对应 mvc action 的 HttpPostedFileBase类型 参数名称 (和action的参数必须相同)
    
                    base64strUrl: "/Home/CompressImgBase64Upload", //大图片压缩上传url
                    imgGreaterCompress: 1024, //图片的大小大于多少进行压缩上传
    
                    limitCount: 2, //(多选上传)一次最多选择上传的图片最大数量
                    maxUploadCount: 3, //最多能上传多少张图片
    
                    maxWidth: 300, //在客户端显示图片最大宽度
                    maxHeight: 300, //在客户端显示图片最大高度
    
                    previewImageClassName: "previewImg previewImg_1", //显示图片的样式类名称
    
                    inputFileImageClassName: "inputFileImage",
    
                    auto: true,
    
                    previewDivId : "preview", //图片预览,动态添加图片预览img标签,添加那个div内
    
                    previewZoom: true,
                    uploadComplete: function (jsonResultData) { //上传成功执行此函数 res是服务端返回的数据
                    var imgArray = document.getElementById(para.previewDivId).getElementsByTagName("img");
                        var finallyImg = document.getElementById(para.previewDivId).getElementsByTagName("img")[imgArray.length-1];
                        if (jsonResultData.Success) {  //上传成功 
                        finallyImg.setAttribute("serverFilePath",jsonResultData.FilePath);  //给动态添加的标签添加 图片上传到服务器图片地址属性
                        console.log("uploadComplete", jsonResultData);
                        uploadCount++;
                        core.checkComplete();
                        } else {   //上传未成功
                        alert("出错了!请重试");
                            finallyImg.remove();  //删除预览图片标签
                        }
                        
                    },
                    uploadError: function (err) {   //上传失败执行此函数
                        console.log("uploadError", err);
                    },
                    onProgress: function (percent) {  // 提供给外部获取单个文件的上传进度,供外部实现上传进度效果
                        console.log('进度条:'+percent);
                    },
                };
                para = $.extend(para, options);
    
                var $self = $(para.filebutton);
                //先加入一个file元素(上传文件按钮)
                var multiple = "";  // 设置多选的参数
                para.multiple ? multiple = "multiple" : multiple = "";
                $self.css('position', 'relative');
                $self.append('<input id="fileImage" class='+para.inputFileImageClassName+'  style="opacity:0;position:absolute;top: 0;left: 0;100%;height:100%" accept="image/*"  type="file" size="30" name="fileselect[]" ' + multiple + '>');
    
                var doms = {
                    "fileToUpload": $self.find("#fileImage"),
                    "thumb": $self.find(".thumb"),
                    "progress": $self.find(".upload-progress")  //进度条
                };
    
                function getBase64Image(img) {
                    var canvas = document.createElement("canvas");
                    canvas.width = img.width;
                    canvas.height = img.height;
    
                    var ctx = canvas.getContext("2d");
                    ctx.drawImage(img, 0, 0, img.width, img.height);
    
                    var dataURL = canvas.toDataURL("image/jpeg");
                    return dataURL;
    
                    // return dataURL.replace("data:image/png;base64,", "");
                }
                function simpleSize(size) {
                    if (!size) return "0";
                    if (size < 1024) {
                        return size;
                    }
                    var kb = size / 1024;
                    if (kb < 1024) {
                        return kb.toFixed(2) + "K";
                    }
                    var mb = kb / 1024;
                    if (mb < 1024) {
                        return mb.toFixed(2) + "M";
    
                    }
                    var gb = mb / 1024;
                    return gb.toFixed(2) + "G";
                };
                //Convert DataURL to Blob to send over Ajax
                function dataURItoBlob(dataUrl) {
                    // convert base64 to raw binary data held in a string
                    // doesn't handle URLEncoded DataURIs - see SO answer #6850276 for code that does this
                    var byteString = atob(dataUrl.split(',')[1]);
    
                    // separate out the mime component
                    var mimeString = dataUrl.split(',')[0].split(':')[1].split(';')[0];
    
                    // write the bytes of the string to an ArrayBuffer
                    var ab = new ArrayBuffer(byteString.length);
                    var ia = new Uint8Array(ab);
                    for (var i = 0; i < byteString.length; i++) {
                        ia[i] = byteString.charCodeAt(i);
                    }
                    return new Blob([ab], { type: 'image/jpeg' });
                }
    
                var uploadCount = 0;
                var core = {
                    fileSelected: function () {
                        var files = $("#fileImage")[0].files;
                        var count = files.length;
                        if (count +document.getElementById(para.previewDivId).getElementsByTagName("img").length> para.maxUploadCount) {  //最大只能上传多少张图片(document.getElementById(para.previewDivId).getElementsByTagName("img").length  获取已经上传了多少张图片)
                               alert("最多只能上传"+para.maxUploadCount+"张图片!");
                               return;
                            }
                        console.log("共有" + count + "个文件");
                        for (var i = 0; i < count; i++) {
                            if (i >= para.limitCount) {   //最大一次性选择多少张图片
                                alert("最多只能选择"+para.limitCount+"张图片!");
                                break;
                            }
                            
                            var item = files[i];
                            console.log("原图片大小", item.size);
    
                            if (item.size > para.imgGreaterCompress) {   //图片打印多少M进行压缩上传
                                console.log("图片大于2M,开始进行压缩...");
    
                                (function(img) {
                                    var mpImg = new MegaPixImage(img);
                                    var resImg = document.getElementById("resultImage");
                                    resImg.file = img;
                                    mpImg.render(resImg, { maxWidth: para.maxWidth, maxHeight: para.maxHeight, quality: 1 }, function() {
                                        var base64 = getBase64Image(resImg);
                                        var base641 = resImg.src;
                                        console.log("base64", base64.length, simpleSize(base64.length), base641.length, simpleSize(base641.length));
                                        if (para.auto) core.uploadBase64str(base64);
                                    });
                                })(item);
    
                            } else {
                                if (para.auto) core.uploadFile(item);
                            }
                            core.previewImage(item);
                        }
                    },
                    uploadBase64str: function (base64Str) {
                        var formdata = new FormData();
                        formdata.append("base64str", base64Str);
                        var xhr = new XMLHttpRequest();
                        xhr.upload.addEventListener("progress", function (e) {
                            var percentComplete = Math.round(e.loaded * 100 / e.total);
                            para.onProgress(percentComplete.toString() + '%');
                        });
                        xhr.addEventListener("load", function (e) {
                        var jsonResultData= JSON.parse(xhr.responseText);
                            para.uploadComplete(jsonResultData);
                        });
                        xhr.addEventListener("error", function (e) {
                            para.uploadError(e);
                        });
    
                        xhr.open("post", para.base64strUrl, true);
                        xhr.send(formdata);
                    },
                    uploadFile: function (file) {
                       // console.log("开始上传");
                        var formdata = new FormData();
    
                        formdata.append(para.filebase, file);//这个名字要和mvc后台配合
    
                        var xhr = new XMLHttpRequest();
                        xhr.upload.addEventListener("progress", function (e) {
    
                            var percentComplete = Math.round(e.loaded * 100 / e.total);
                            para.onProgress(percentComplete.toString() + '%');
                        });
                        xhr.addEventListener("load", function (e) {
                            var jsonResultData= JSON.parse(xhr.responseText);
                            para.uploadComplete(jsonResultData);
                        });
                        xhr.addEventListener("error", function (e) {
                            para.uploadError(e);
                        });
    
                        xhr.open("post", para.url, true);
                        xhr.send(formdata);
                    },
                    checkComplete:function() {
                        var all = (doms.fileToUpload)[0].files.length;
                        if (all == uploadCount) {
                            console.log(all + "个文件上传完毕");
                            doms.fileToUpload.remove();   //删除上次动态添加 上传按钮
                            //input有一个问题就是选择重复的文件不会触发change事件,所以做了一个处理,再每次上传完之后删掉这个元素再新增一个input。
                            $self.append('<input id="fileImage" class='+para.inputFileImageClassName+'  style="opacity:0;position:absolute;top: 0;left: 0;100%;height:100%" accept="image/*"  type="file" size="30" name="fileselect[]" ' + multiple + '>');
    
                        }
                    },
                    uploadFiles: function () {
                        var files = (doms.fileToUpload)[0].files;
                        for (var i = 0; i < files.length; i++) {
                            core.uploadFile(files[i]);
                        }
                    },
                    previewImage: function (file) {   //将压缩后的图片显示
                        if (!para.previewZoom) return;
                        var img = document.createElement("img");
                        img.file = file;
                        img.className =para.previewImageClassName;  //样式类名称 (添加一个)
                        //img.className = "previewImg previewImg_1"; //样式类名称(添加两个)
                        //img.classList.add("previewImg");  //添加样式类
                        //img.classList.add("previewImg_1"); //添加样式类
                        //img.setAttribute("test","aaa");
                        $(para.previewZoom).append(img);
                        // 使用FileReader方法显示图片内容
                        var reader = new FileReader();
                        reader.onload = (function (aImg) {
                            return function (e) {
                                aImg.src = e.target.result;
                            };
                        })(img);
                        reader.readAsDataURL(file);
                    }
                }
                $(document).on("change", "#fileImage", function () {
                    core.fileSelected();
                });
    
                $(document).on("click", para.filebutton, function () {
                    console.log("clicked");
                });
                if (para.uploadButton) {
                    $(document).on("click", para.uploadButton, function () {
                        core.uploadFiles();
                    });
                }
            }
        });
    })(Zepto);

    视图页面代码:

    @{
        ViewBag.Title = "h5图片压缩与上传";
    }
    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8">
    </head>
    <style>
        .filePicker
        {
            background: none repeat scroll 0 0 #00B7EE;
            border-radius: 3px;
            box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
            color: #FFFFFF;
            cursor: pointer;
            display: inline-block;
            font-size: 18px;
            height: 44px;
            line-height: 44px;
            width: 90%;
            min-width: 120px;
            margin: 0 auto 0px;
            overflow: hidden;
            transition: background 0.2s;
            -moz-transition: background 0.2s;
            -webkit-transition: background 0.2s;
            -o-transition: background 0.2s;
        }
        #preview
        {
            margin-top: 30px;
        }
        #preview img
        {
            max-width: 30%;
        }
        
        .previewImg {
            border:5px solid red;
        }
        
      .previewImg_1 {
            min-width: 120px;
        } 
        
        .inputFileImage {
             border:5px solid red;
        }
    </style>
    <body>
    <div class="page" id="upload">
        <h2>
            测试H5图片压缩与上传</h2>
        <div id="dd" class="filePicker">
            点击选择文件</div>
        <img id="resultImage" style="display: none;"/>
        <div id="preview">
        </div>
    </div>
    <button onclick="GetServerFilePath('preview')">获取上传到服务端图片路径</button>
        <script src="/Scripts/zepto.min.js" type="text/javascript"></script>
        <script src="/Scripts/h5upload.js" type="text/javascript"></script>
        <script>
            var para = { filebutton: "#dd", previewZoom: "#preview", previewDivId: "preview" };
            $.fileUpload(para);
    
            function GetServerFilePath(previewDivId) {
                var serverFilePathArray = []; //存储服务端返回图片路径
                var imgArray = document.getElementById(previewDivId).getElementsByTagName("img");
                for (var i = 0; i < imgArray.length; i++) {
                    serverFilePathArray.push(imgArray[i].getAttribute("serverFilePath"));
                }
                alert(serverFilePathArray.join(","));
                return serverFilePathArray;
            }
           
    </script>
    </body>
    </html>

    控制器代码:

    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Web;
    using System.Web.Mvc;
    
    namespace H5UploadDemo.Controllers
    {
        public class HomeController : Controller
        {
            //
            // GET: /Home/
    
            public ActionResult Index()
            {
                return View();
            }
    
    
            /// <summary>
            /// 未压缩上传
            /// </summary>
            /// <param name="mfile"></param>
            /// <returns></returns>
            [HttpPost]
            public ActionResult NotCompressUploadImg(HttpPostedFileBase mfile)
            {
                if (mfile != null)
                {
                    var path = "/Upload/Image/";
                    var uploadpath = Server.MapPath("~"+path);
                    if (!Directory.Exists(uploadpath))
                    {
                        Directory.CreateDirectory(uploadpath);
                    }
                    string fileName = Path.GetFileName(mfile.FileName);// 原始文件名称
                    string fileExtension = Path.GetExtension(fileName); // 文件扩展名
                    string saveName = Guid.NewGuid() + fileExtension; // 保存文件名称 这是个好方法。
                    //string saveName = Encrypt.GenerateOrderNumber() + fileExtension; // 保存文件名称 这是个好方法。
                    mfile.SaveAs(uploadpath + saveName);
    
                    return Json(new { Success = true, FilePath = path + saveName, Message = "上传成功!" });
                }
                return Json(new { Success = false, Message = "请选择要上传的文件!" }, JsonRequestBehavior.AllowGet);
            }
    
            /// <summary>
            /// 压缩上传
            /// </summary>
            /// <param name="base64str"></param>
            /// <returns></returns>
            [HttpPost]
            public ActionResult CompressImgBase64Upload(string base64str)
            {
                try
                {
                    var imgData = base64str.Split(',')[1];
                    //过滤特殊字符即可   
                    string dummyData = imgData.Trim().Replace("%", "").Replace(",", "").Replace(" ", "+");
                    if (dummyData.Length % 4 > 0)
                    {
                        dummyData = dummyData.PadRight(dummyData.Length + 4 - dummyData.Length % 4, '=');
                    }
                    byte[] byteArray = Convert.FromBase64String(dummyData);
                    using (System.IO.MemoryStream ms = new System.IO.MemoryStream(byteArray))
                    {
                        var img = System.Drawing.Image.FromStream(ms);
                        var path = "/Upload/Image/";
                        var uploadpath = Server.MapPath("~" + path);
                        if (!Directory.Exists(uploadpath))
                        {
                            Directory.CreateDirectory(uploadpath);
                        }
                        var saveName =   Guid.NewGuid() + ".jpg";
                        img.Save(uploadpath+saveName);
                        return Json(new { Success = true, FilePath = path + saveName, Message = "上传成功!" });
                    }
                }
                catch (Exception e)
                {
                    return Json(new { Success = false, FilePath ="", Message = "出错了!" });
    
                }
            }
    
        }
    }

    完整代码地址:http://pan.baidu.com/s/1c2LuVDm 密码:gwdc

  • 相关阅读:
    环境变量
    多重继承
    参数检查(@property)
    限制属性绑定(__slots__)
    实例属性和类属性
    2017-11-28 中文编程语言之Z语言初尝试: ZLOGO 4
    2017-10-23 在各种编程语言中使用中文命名
    2017-11-27 汉化了十数个编译器的前辈的心得体会
    五行
    阴阳
  • 原文地址:https://www.cnblogs.com/linJie1930906722/p/5975160.html
Copyright © 2011-2022 走看看