欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

ASP.NET MVC中的图片压缩、图片水印

程序员文章站 2022-06-11 22:08:01
...

一、在做移动端图片上传的时候,有的图片相对来说比较大,直接上传完整的图片显然不是一个好办法,所以直接在前端压缩图片,已经成为了很多移动端图片上传的必备功能;

在做图片压缩并且上传是主要用到filereader、canvas和formdata;

图片压缩上传的过程并不复杂,搞懂它的逻辑就行,总共可以分成三步:

1.用户在使用input file控件上传图片的时候,首先使用filereader读取用户上传的图片数据(是base64格式);

2.将读取的图片数据传入img对象,然后将img绘制到canvas上,在调用canvas.toDataURL对图片进行压缩;

   在这一步,我们需要考虑几个问题:

          1).首先是图片的大小,如果图片超过两百万像素是无法绘制到canvas上的,在这时候调用drawImage虽然不会报错,但是调用toDataURL方法获取图片数据时获取到的是空的图片数据;==》解决方法:使用瓦片绘制,将如片分割成多块绘制到canvas上,我是将图片分割成100万像素一块,再绘制到canvas上;

          2).其次就是canvas的大小也有限制,如果canvas的大小大于五百万像素,不仅图片画不出来,其他的什么也都画不出来;==》解决方法:对图片的像素大小进行压缩,如果图片大于四百万像素,计算压缩比并将大小压至400万以下;

          3).还有要注意的点就是:如果要用canvas的toDataURL方法的话,toDataURL是只能压缩jpg格式的,当用户上传其他格式的图片的时候,就需要转换为jpg格式,用canvas.toDataURL('image/jpeg', 0.1); 将类型统一设为jpeg格式的;

3.获取压缩后的base64格式的图片数据,转为二进制塞入formdata中进行上传;

首先,前台的代码为:

<!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>移动端图片压缩上传</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 {
            width: 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;
                width: 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;
            width: 100%;
            height: 20px;
            line-height: 20px;
            bottom: 0;
            left: 0;
            background-color: rgba(100,149,198,.5);
        }

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

        .size {
            position: absolute;
            width: 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-width: 30px;
                    max-height: 30px;
                    margin: -4px 0 0 10px;
                }
    </style>
</head>
<body>
    <table>
        <tr>
            <td style="width:60%">
                <input type="file" id="choose" accept="image/*" multiple>
                <ul class="img-list"></ul>
                <a id="upload">上传图片</a>
                <span class="tips">只允许上传jpg、png及gif</span>
                <div class="pic-list">
                    你上传的图片(图片有效期为1分钟):
                </div>
            </td>
            <td style="width:40%">

            </td>
        </tr>
    </table>
</body>
</html>

前端页面的脚本:

 <script src="~/Scripts/jquery-1.10.2.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 = 100 * 1024;

          $("#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 + ")");

                //如果图片大小小于100kb,则直接上传
                if (result.length <= maxsize) {
                  img = null;
                  upload(result, file.type, $(li));
                  return;
                }
                //图片加载完毕之后进行压缩,然后上传
                if (img.complete) {
                  callback();
                } else {
                  img.onload = callback;
                }
                function callback() {
                  var data = compress(img);
                  upload(data, file.type, $(li));
                  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);
              }

            //进行最小压缩
            var ndata = canvas.toDataURL('image/jpeg', 0.1);
            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, $li) {
            var text = window.atob(basestr.split(",")[1]);
            var buffer = new Uint8Array(text.length);
            var pecent = 0, loop = null;

              for (var i = 0; i < text.length; i++)
              {
                buffer[i] = text.charCodeAt(i);
               }

            var blob = getBlob([buffer], type);
            var xhr = new XMLHttpRequest();
              var formdata = getFormData();

              //文本域名称 imagefile
              formdata.append('imagefile', blob);

              // 后台控制器 /cupload/index
              xhr.open('post', '/cupload/index');

            xhr.onreadystatechange = function() {
              if (xhr.readyState == 4 && xhr.status == 200) {
                var jsonData = JSON.parse(xhr.responseText);
                var imagedata = jsonData[0] || {};
                var text = imagedata.path ? '上传成功' : '上传失败';
                console.log(text + ':' + imagedata.path);
                clearInterval(loop);
                //当收到该消息时上传完毕
                $li.find(".progress span").animate({'width': "100%"}, pecent < 95 ? 200 : 0, function() {
                  $(this).html(text);
                });
                if (!imagedata.path) return;
                $(".pic-list").append('<a href="' + imagedata.path + '">' + imagedata.name + '(' + imagedata.size + ')<img src="' + imagedata.path + '" /></a>');
              }
            };
            //数据发送进度,前50%展示该进度
            xhr.upload.addEventListener('progress', function(e) {
              if (loop) return;
              pecent = ~~(100 * e.loaded / e.total) / 2;
              $li.find(".progress span").css('width', pecent + "%");
              if (pecent == 50) {
                mockProgress();
              }
            }, false);
            //数据后50%用模拟进度
            function mockProgress() {
              if (loop) return;
              loop = setInterval(function() {
                pecent++;
                $li.find(".progress span").css('width', pecent + "%");
                if (pecent == 99) {
                  clearInterval(loop);
                }
              }, 100)
            }
            xhr.send(formdata);
          }
          /**
           * 获取blob对象的兼容性写法
           * param buffer
           * param format
           * returns {*}
           */
          function getBlob(buffer, format) {
            try {
              return new Blob(buffer, {type: format});
            } catch (e) {
              var bb = new (window.BlobBuilder || window.WebKitBlobBuilder || window.MSBlobBuilder);
              buffer.forEach(function(buf) {
                bb.append(buf);
              });
              return bb.getBlob(format);
            }
          }
          /**
           * 获取formdata
           */
          function getFormData() {
            var isNeedShim = ~navigator.userAgent.indexOf('Android')
                && ~navigator.vendor.indexOf('Google')
                && !~navigator.userAgent.indexOf('Chrome')
                && navigator.userAgent.match(/AppleWebKit\/(\d+)/).pop() <= 534;
            return isNeedShim ? new FormDataShim() : new FormData()
          }
          /**
           * formdata 补丁, 给不支持formdata上传blob的android机打补丁
           * constructor
           */
          function FormDataShim() {
            console.warn('using formdata shim');
            var o = this,
                parts = [],
                boundary = Array(21).join('-') + (+new Date() * (1e16 * Math.random())).toString(36),
                oldSend = XMLHttpRequest.prototype.send;
            this.append = function(name, value, filename) {
              parts.push('--' + boundary + '\r\nContent-Disposition: form-data; name="' + name + '"');
              if (value instanceof Blob) {
                parts.push('; filename="' + (filename || 'blob') + '"\r\nContent-Type: ' + value.type + '\r\n\r\n');
                parts.push(value);
              }
              else {
                parts.push('\r\n\r\n' + value);
              }
              parts.push('\r\n');
            };
            // Override XHR send()
            XMLHttpRequest.prototype.send = function(val) {
              var fr,
                  data,
                  oXHR = this;
              if (val === o) {
                // Append the final boundary string
                parts.push('--' + boundary + '--\r\n');
                // Create the blob
                data = getBlob(parts);
                // Set up and read the blob into an array to be sent
                fr = new FileReader();
                fr.onload = function() {
                  oldSend.call(oXHR, fr.result);
                };
                fr.onerror = function(err) {
                  throw err;
                };
                fr.readAsArrayBuffer(data);
                // Set the multipart content type and boudary
                this.setRequestHeader('Content-Type', 'multipart/form-data; boundary=' + boundary);
                XMLHttpRequest.prototype.send = oldSend;
              }
              else {
                oldSend.call(this, val);
              }
            };
          }
    </script>

后端代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace WebApplication102.Controllers
{
    public class cuploadController : Controller
    {
        // GET: cupload
        public ActionResult Index(HttpPostedFileBase imagefile)
        {
            Guid guid = Guid.NewGuid();
            string path = Server.MapPath("~/Upload/"+guid.ToString("N")+".jpg");
            imagefile.SaveAs(path);
            return View();
        }
    }
}

二、图片水印

工作或者是生活中,有时候需要为图片添加水印,这样可以有效的保护图片被别人盗用,那么怎么快速为图片添加水印呢?

        public FileResult DownLoad(string path)
        {
            //图片的物理路径
            string NewPath = Server.MapPath(path);
            //加载图片
            Image img = Image.FromFile(NewPath);
            //获取图片的编辑工具
            Graphics g = Graphics.FromImage(img);
            g.DrawString("aaaaaaaa", new Font("宋体", 12), Brushes.Red, new PointF(50, 50));

            MemoryStream ms = new MemoryStream();
            img.Save(ms, ImageFormat.Jpeg);
            byte[] bytes = ms.GetBuffer();

            return File(bytes, "image/jpeg", "AAA");
        }