C#MVC和cropper.js实现剪裁图片ajax上传的弹出层
首先使用cropper.js插件,能够将剪裁后的图片返回为base64编码,后台根据base64编码解析保存图片。
jQuery.cropper:
是一款使用简单且功能强大的图片剪裁jquery插件。该图片剪裁插件支持图片放大缩小,支持图片旋转,支持触摸屏设备,支持canvas,并且支持跨浏览器使用
网站:http://fengyuanchen.github.io/cropper/
可以自己搜索中文API
前台代码:
@{ Layout = null; } <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>上传图片</title> <script src="~/Content/js/jquery.min.js"></script> <link rel="stylesheet" type="text/css" href="http://apps.bdimg.com/libs/bootstrap/3.3.4/css/bootstrap.css"> <link href="~/Content/css/cropper.min.css" rel="stylesheet" /> <link href="~/Content/css/sitelogo.css" rel="stylesheet" /> <link rel="stylesheet" type="text/css" href="http://cdn.bootcss.com/font-awesome/4.6.0/css/font-awesome.min.css"> <script src="~/Content/js/bootstrap.min.js"></script> <script src="~/Content/js/cropper.js"></script> <script src="~/Content/js/sitelogo.js"></script> <style type="text/css"> .avatar-btns button { height: 35px; } </style> </head> <body> <button type="button" class="btn btn-primary" data-toggle="modal" data-target="#avatar-modal" style="margin: 10px;">修改头像</button> <div class="user_pic" style="margin: 10px;"> <img src="" /> </div> <div class="modal fade" id="avatar-modal" aria-hidden="true" aria-labelledby="avatar-modal-label" role="dialog" tabindex="-1"> <div class="modal-dialog modal-lg"> <div class="modal-content"> <div class="avatar-form" @*action="" enctype="multipart/form-data" method="post"*@ > @* <form class="avatar-form">*@ <div class="modal-header"> <button class="close" data-dismiss="modal" type="button">×</button> <h4 class="modal-title" id="avatar-modal-label">上传图片</h4> </div> <div class="modal-body"> <div class="avatar-body"> <div class="avatar-upload"> <input class="avatar-src" name="avatar_src" type="hidden"> <input class="avatar-data" name="avatar_data" type="hidden"> <label for="avatarInput" style="line-height: 35px;">图片上传</label> <button class="btn btn-danger" type="button" style="height: 35px;" onclick="$('input[id=avatarInput]').click();">请选择图片</button> <span id="avatar-name"></span> <input class="avatar-input hide" id="avatarInput" name="avatar_file" type="file"> </div> </div> <div class="row"> <div class="col-md-9"> <div class="avatar-wrapper"></div> </div> <div class="col-md-3"> <div class="avatar-preview preview-lg" id="imageHead"></div> <!--<div class="avatar-preview preview-md"></div> <div class="avatar-preview preview-sm"></div>--> </div> </div> <div class="row avatar-btns"> <div class="col-md-4"> <div class="btn-group"> <button class="btn btn-danger fa fa-undo" data-method="rotate" data-option="-90" type="button" title="Rotate -90 degrees">向左旋转</button> </div> <div class="btn-group"> <button class="btn btn-danger fa fa-repeat" data-method="rotate" data-option="90" type="button" title="Rotate 90 degrees">向右旋转</button> </div> </div> <div class="col-md-5" style="text-align: right;"> <button class="btn btn-danger fa fa-arrows" data-method="setDragMode" data-option="move" type="button" title="移动"> <span class="docs-tooltip" data-toggle="tooltip" title="" data-original-title="$().cropper("setDragMode", "move")"></span> </button> <button type="button" class="btn btn-danger fa fa-search-plus" data-method="zoom" data-option="0.1" title="放大图片"> <span class="docs-tooltip" data-toggle="tooltip" title="" data-original-title="$().cropper("zoom", 0.1)"> <!--<span class="fa fa-search-plus"></span>--> </span> </button> <button type="button" class="btn btn-danger fa fa-search-minus" data-method="zoom" data-option="-0.1" title="缩小图片"> <span class="docs-tooltip" data-toggle="tooltip" title="" data-original-title="$().cropper("zoom", -0.1)"> <!--<span class="fa fa-search-minus"></span>--> </span> </button> <button type="button" class="btn btn-danger fa fa-refresh" data-method="reset" title="重置图片"> <span class="docs-tooltip" data-toggle="tooltip" title="" data-original-title="$().cropper("reset")" aria-describedby="tooltip866214"></span> </button> </div> <div class="col-md-3"> <button class="btn btn-danger btn-block avatar-save fa fa-save" type="button" data-dismiss="modal">保存修改</button> </div> </div> </div> </div> </div> </div> </div> <script src="~/Content/js/html2canvas.min.js" type="text/javascript" charset="utf-8"></script> <script type="text/javascript"> //做个下简易的验证 大小 格式 $('#avatarInput').on('change', function (e) { var filemaxsize = 1024 * 5;//5M var target = $(e.target); var Size = target[0].files[0].size / 1024;//获取image的尺寸 if (Size > filemaxsize) { alert("图片过大,请重新选择(<5M)"); $(".avatar-wrapper").empty();//清除 return false; }//验证格式png,jpeg if (!this.files[0].type.match(/image.(png|jpeg)/)) { alert("请选择正确的图片(.jpg/.png/.jpeg)"); } else { var filename = document.querySelector("#avatar-name"); var texts = document.querySelector("#avatarInput").value; var teststr = texts; //你这里的路径写错了 testend = teststr.match(/[^\\]+\.[^\(]+/i); //直接完整文件名的 filename.innerHTML = testend; } }); //保存 $(".avatar-save").on("click", function () { var img_lg = document.getElementById('imageHead'); // 截图小的显示框内的内容 html2canvas(img_lg, { allowTaint: true, taintTest: false, onrendered: function (canvas) { canvas.id = "mycanvas"; //生成base64图片数据 var type = document.getElementById('avatarInput').files[0].type; var dataUrl = canvas.toDataURL(type); imagesAjax(dataUrl); } }); }); //ajax提交 function imagesAjax(src) { var data = {}; data.img = src; $.ajax({ url: '@Url.Action("UploadBase")', data: data, type: "POST", dataType: 'text', success: function (result) { $('.user_pic img').attr('src', result); }, error: function (x, s, e) { //请求出错处理 alert(x.status + " " + s + " " + e); } }); } </script> </body> </html>
后台代码:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using System.IO; using System.Xml; using System.Xml.Schema; using System.Drawing; using System.Net; using System.Net.Http; using System.Runtime.Serialization.Formatters.Binary; using System.Web.Http; namespace MvcDemo.Controllers { public class UploadImagesController : Controller { // // GET: /UploadImages/ public ActionResult Index() { return View(); } public ActionResult Upload() { return View(); } /// <summary> /// 接收前台传来的Base64编码 /// </summary> public string UploadBase(string img) { string path = Common.UploadFile.Upload(img); return path; } public ActionResult UploadImg() { //上传文件 HttpPostedFileBase img = Request.Files["btnfile"]; string s = img.FileName; string fileExtension = Path.GetExtension(s); string path = "/Temp/"; if (Directory.Exists(Server.MapPath(path)) == false)//如果不存在就创建file文件夹 { Directory.CreateDirectory(Server.MapPath(path)); } string virpath = path + Guid.NewGuid() + fileExtension; img.SaveAs(Server.MapPath(virpath)); return Content(virpath); } } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Drawing; using System.IO; namespace Common { /// <summary> /// 上传文件公共类 /// </summary> public static class UploadFile { //静态 当前项目的路径 private static string rootPath = AppDomain.CurrentDomain.BaseDirectory; //静态 要保存的路径 private static string path = "/UploadImages/"; /// <summary> /// 接收Base64编码格式的图片 /// </summary> /// <param name="img">图片Base64字符串</param> /// <returns>图片路径</returns> public static string Upload(string img) { if (!IsBase64(img))//判断是否是base64字符串 return null; //获取文件后缀,判断格式 string suffix = GetSuffixFromBase64Str(img); string pattren = @"png|jpeg+$";//正则验证 if (!System.Text.RegularExpressions.Regex.IsMatch(suffix, pattren)) { return null; } //如果不存在就创建file文件夹 if (Directory.Exists(rootPath + path) == false) { Directory.CreateDirectory(rootPath + path); } //防止同名的两种方法,一是获取当前时间与指定时间的毫秒差作为名字 //而是通过唯一的GUID //图片名 //string datetime = GetTimeStamp(); // 文件夹 图片名 后缀名 //string dbPath = path + GetTimeStamp() + suffix; // 生成一个新的 GUID 唯一值 string dbPath = path + Guid.NewGuid() + "." + suffix; //保存路径 string savePath = rootPath + dbPath; try { //截取base64串 //string strBase = img.Split(',')[1]; //获取图片并保存 Base64ToImg(img.Split(',')[1]).Save(savePath); } catch (Exception) { return null; } return dbPath; } /// <summary> /// 删除文件 /// </summary> /// <param name="fileUrl">路径</param> public static void DeleteImgFile(string fileUrl) { string file = System.Web.HttpContext.Current.Server.MapPath(fileUrl); //文件是否存在 if (File.Exists(file)) { File.Delete(file); } } //解析base64编码获取图片 private static Bitmap Base64ToImg(string base64Code) { MemoryStream stream = new MemoryStream(Convert.FromBase64String(base64Code)); return new Bitmap(stream); } //获取当前时间段额时间戳 private static string GetTimeStamp() { TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0); return Convert.ToInt64(ts.TotalMilliseconds).ToString(); } //判读是否是base64编码 private static char[] base64CodeArray = new char[] { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/', '=' }; /// <summary> /// 是否base64字符串 /// </summary> /// <param name="base64Str">要判断的字符串</param> /// <returns></returns> private static bool IsBase64(string base64Str) { byte[] bytes = null; return IsBase64(base64Str, out bytes); } /// <summary> /// 是否base64字符串 /// </summary> /// <param name="base64Str">要判断的字符串</param> /// <param name="bytes">字符串转换成的字节数组</param> /// <returns></returns> private static bool IsBase64(string base64Str, out byte[] bytes) { //string strRegex = "^([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{4}|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==)$"; bytes = null; if (string.IsNullOrEmpty(base64Str)) return false; else { if (base64Str.Contains(",")) base64Str = base64Str.Split(',')[1]; if (base64Str.Length % 4 != 0) return false; if (base64Str.Any(c => !base64CodeArray.Contains(c))) return false; } try { bytes = Convert.FromBase64String(base64Str); return true; } catch (FormatException) { return false; } } /// <summary> /// 把base64字符串转换成Bitmap /// </summary> /// <param name="base64Str">要转换的base64字符串</param> /// <returns></returns> private static Bitmap Base64ToBitmap(string base64Str) { Bitmap bitmap = null; byte[] bytes = null; try { if (IsBase64(base64Str, out bytes)) { using (MemoryStream stream = new MemoryStream(bytes)) { stream.Seek(0, SeekOrigin.Begin);//为了避免有时候流指针定位错误,显式定义一下指针位置 bitmap = new Bitmap(stream); } } } catch (Exception) { bitmap = null; } return bitmap; } /// <summary> /// 根据base64字符串获取文件后缀(图片格式) /// </summary> /// <param name="base64Str">base64</param> /// <returns></returns> private static string GetSuffixFromBase64Str(string base64Str) { string suffix = string.Empty; string prefix = "data:image/"; if (base64Str.StartsWith(prefix) && base64Str.Contains(";") && base64Str.Contains(",")) { base64Str = base64Str.Split(';')[0]; suffix = base64Str.Substring(prefix.Length); } return suffix; } } }
Data URI scheme
Data URI scheme是在RFC2397中定义的,目的是将一些小的数据,直接嵌入到网页中,从而不用再从外部文件载入。 在上面的Data URI中,data表示取得数据的协定名称,image/png 是数据类型名称,base64 是数据的编码方法,逗号后面就是这个image/png文件base64编码后的数据。 支持的格式:data:image/png;base64,base64编码的png图片数据 data:image/jpeg;base64,base64编码的jpeg图片数据 把图像文件的内容直接写在了HTML 文件中,这样做的好处是,节省了一个HTTP 请求。坏处是浏览器不会缓存这种图像。 Jpg和jpeg格式,扩展名的实质是相同的
GUID
GUID(全球唯一标识符) 是一种由算法生成的二进制长度为128位的数字标识符,在理想情况下,任何计算机和计算机集群都不会生成两个相同的GUID,。随机生成两个相同GUID的可能性是非常小的,但并不为0。所以,用于生成GUID的算法通常都加入了非随机的参数(如时间),以保证这种重复的情况不会发生
这是通过ajax直接上传的没有经过剪裁: https://www.cnblogs.com/bubugao/p/MyAjaxForm.html