在作微信公众号或者企业微信开发业务应用的时候,咱们经常会涉及到图片预览、上传等的处理,每每业务需求不止一张图片,所以相对来讲,须要考虑的全面一些,用户还须要对图片进行预览和相应的处理,在开始的时候我使用JSSDK方式,使用微信的SDK接口进行图片的上传、预览操做,后来发现经过URL.createObjectURL选定本地图片预览、上传也是很是方便的,本篇随笔针对同一个多图片的业务需求,使用JSSDK和URL.createObjectURL两种方式进行图片预览、上传、删除等常规的处理。javascript
在一个公众号页面-问诊界面里面,咱们须要让用户上传相关的图片,包括症状图片、处方图片等,每一个列表能够上传多张图片,以下界面所示。html
这里使用了SDK进行图片的上传处理,参考Weui的上传样式,选择本地几张图片,能够看到缩略图展现在图框里面,可是图片尚未上传,咱们在保存问诊信息的时候,才启动图片文件的上传处理。java
若是图片是在编辑界面中,咱们须要考虑对现有图片进行删除的处理,删除前确认便可。 web
单击删除图标的按钮,提示用户进行图片删除确认便可。ajax
以上就是咱们几个图片处理的场景,咱们来看看如何实现的。数据库
咱们以症状图片为例,它的界面HTML部分的代码以下所示。json
<div class="weui-cells__title">症状图片</div> <div class="weui-cells weui-cells_form"> <div class="weui-cell"> <div class="weui-cell__bd"> <div class="weui-uploader"> <!--编辑的时候,放置已有图片进行预览--> <ul class="weui-weui-uploader__files" style="list-style-type: none" id="imgSickPreview"></ul> <div class="weui-uploader__bd"> <!--放置选择的图片进行预览--> <ul class="weui-weui-uploader__files" style="list-style-type: none" id="imgSick"></ul> <div class="weui-uploader__input-box"> <!--图片上传的图标处理--> <span id="uploaderSick" class="weui-uploader__input"></span> </div> </div> </div> </div> </div> </div>
为了使用微信JSSDK来实现上传、预览图片的功能,咱们须要定义好对应的JS接口,以下代码所示。数组
<script language="javascript"> var appid = '@ViewBag.appid'; var noncestr = '@ViewBag.noncestr'; var signature = '@ViewBag.signature'; var timestamp = '@ViewBag.timestamp'; wx.config({ debug: false, appId: appid, // 必填,公众号的惟一标识 timestamp: timestamp, // 必填,生成签名的时间戳 nonceStr: noncestr, // 必填,生成签名的随机串 signature: signature, // 必填,签名,见附录1 jsApiList: [ 'checkJsApi', 'chooseImage', 'previewImage', 'uploadImage', 'downloadImage', 'getLocalImgData' ] }); ...... </script>
在上传图片以前,咱们须要经过JSSDK的方式选择图片,这里用到了chooseImage的接口,大概所需的代码以下所示。浏览器
//上传图片集合[用微信上传的时候,记录微信mediaId集合] var images = { localSickId: [],//病情 localPresId: [],//处方 serverSickId: [], serverPresId: [] }; //图片选择 $("#uploaderSick").click(function () { chooseImage("imgSick", "sick"); }); $("#uploaderPres").click(function () { chooseImage("imgPres", "pres"); }); //选择图片显示 function chooseImage(ctrlName, type) { //清空集合 if (type == "sick") { images.localSickId = []; } else { images.localPresId = []; } wx.chooseImage({ count: 3, // 默认9 sizeType: ['original', 'compressed'], // 能够指定是原图仍是压缩图,默认两者都有 sourceType: ['album', 'camera'], // 能够指定来源是相册仍是相机,默认两者都有 success: function (res) { var ctrl = $("#" + ctrlName); ctrl.html("");//清空图片显示 //localIds = res.localIds; // 返回选定照片的本地ID列表,localId能够做为img标签的src属性显示图片 if (type == "sick") { images.localSickId = res.localIds; } else { images.localPresId = res.localIds; } //动态增长img标识 $.each(res.localIds, function (index, item) { ctrl.append("<img class='weui-uploader__file' src='" + item + "' />"); }); } }); }
选择图片后,就是将图片的缩略图动态的增长在指定图片框里面。而后在保存数据的时候,使用JSSDK提交图片到微信服务器,咱们服务器后台再从微信服务器获取图片(经过媒体id)。服务器
这里咱们定义了两类的图片,方便区分,分别是症状图片和处方图片,所以须要定义两个类别的变量,分别存储本地和服务器返回的id集合。
咱们在进行表单提交的时候,须要确认一些必填项,而后在检查是否有文件须要上传,若是有则执行上传处理后提交表单,大概的处理代码以下所示。
//上传数据 $("#btnOK").click(function () { var PatientName = $("#PatientName").val(); if (PatientName == '' || PatientName == undefined) { $.toast('患者姓名不能为空', "forbidden"); return; } var ProblemDetail = $("#ProblemDetail").val(); if (ProblemDetail == '' || ProblemDetail == undefined) { $.toast('详细描述不能为空', "forbidden"); return; } //上传图片 if (images.localSickId.length > 0 || images.localPresId.length > 0) { uploadImage(submitCallback);//经过就提交数据 } else { submitCallback(); } });
这里主要的图片上传处理,就是 uploadImage 函数的处理了,而submitCallback这是定义一个函数上传表单数据的。
因为微信JSSDK上传图片,是一个个上传的,咱们须要把它们串联起来,一并上传。uploadImage 里面定义了一个内部函数,依次进行图片的上传。
咱们经过序号来标识两类图片,图片上传成功后,咱们把图片媒体的id(JSSDK返回的)记录下来,统一提交给对应数据库记录,在后台进行图片文件的提取便可。
//上传图片 function uploadImage(callback) { var localIds = images.localSickId.concat(images.localPresId);//合并数组 var i = 0, length = localIds.length; //$.toast(length); images.serverSickId = []; images.serverPresId = []; //定义一个子级函数,方便递归调用 function upload(callback) { wx.uploadImage({ localId: localIds[i], success: function (res) { i++; //成功后加入不一样的集合 if (i <= images.localSickId.length) { images.serverSickId.push(res.serverId);//第一部分 } else { images.serverPresId.push(res.serverId);//第二部分 } if (i < length) { upload(callback); }else if (callback != undefined) { callback();//回调函数 } }, fail: function (res) { alert(JSON.stringify(res)); } }); }; upload(callback); }
其中咱们的定义的callback函数,是用来最后上传完成后,执行表单的记录存储的,表单包含各类输入和图片的ID信息,以下是详细的表单保存操做代码。
//在上传图片后触发的回调函数 function submitCallback() { var druglist = [];//构造集合对象 for (var key in itemDict) { //Drug_ID,DrugName,How,Freq druglist.push({ 'Drug_ID': key, "DrugName": itemDict[key], 'How': howDict[key], 'Freq': freqDict[key], 'Quantity': quantityDict[key] }); } var url = "/H5/DrugInquirySave?openid=@ViewBag.openid"; var postData = { PatientName: $("#PatientName").val(), Gender: $("#Gender").val(), BirthDate: $("#BirthDate").val(), Telephone: $("#Telephone").val(), ProblemDetail: $("#ProblemDetail").val(), Creator: $("#Creator").val(), ProblemItems: $("input[name='ProblemItems']:checked").val(), @if (ViewBag.Info != null) { <text> ID: '@ViewBag.Info.ID', </text> } SickAttachGUID: $("#SickAttachGUID").val(), PresAttachGUID: $("#PresAttachGUID").val(), ServerSickId: JSON.stringify(images.serverSickId), ServerPresId: JSON.stringify(images.serverPresId), DrugList: JSON.stringify(druglist) }; $.post(url, postData, function (json) { //转义JSON为对象 var data = $.parseJSON(json); if (data.Success) { $.toast("处方已提交审核中,稍后请处处方查询查看。"); //WeixinJSBridge.call('closeWindow');//关闭窗口 location.href = "/h5/Prescription";//跳转处处方页面 } else { $.toast("保存失败:" + data.ErrorMessage, "forbidden"); } }); };
咱们注意到,咱们服务端返回的ID集合,咱们分别放在了两个字段里面提交到后台处理。
ServerSickId: JSON.stringify(images.serverSickId),
ServerPresId: JSON.stringify(images.serverPresId),
在后台,咱们首先须要提取用户提交的基础表单数据,以下是后台定义的函数处理
这些是常规的表单信息,咱们提交到微信服务器的图片信息也须要提取出来的,这些图片分两类,每类都包含多个字符串组成的图片ID集合。
后台主要就是根据这些ID,使用微信基础接口,获取临时图片的接口方式,把图片从服务器上下载下来存储到本地服务器上。
其中UploadFile函数就是封装了如何实现图片获取、图片存储的处理逻辑,主要的代码部分逻辑以下所示。
这种方式很好的利用了JSSDK的图片选择、上传的处理,实现了咱们所须要的图片预览、选择、上传等一系列操做,也可以知足实际的功能须要。
不过总感受把图片绕了一圈再回来不太好而已。
前面介绍了使用微信JSSDK方式实现图片预览、选择、上传等一系列操做,在上传文件的时候,感受绕了一圈再回来,一直但愿可以直接把文件直接提交到服务器上更好,就像咱们通常的Web应用上传附件同样感受更好一些,后来发现了能够经过URL.createObjectURL进行相关的处理,参考了一些案例,对前面介绍的JSSDK的图片上传方式进行改良,从而实现了把图片附件经过表单的方式直接提交到本身后台服务器上了,下面开始介绍一下这种方式的思路和实现代码。
首先咱们定义一个预览图片的列表和一个Input的文件控件元素,替代前面的作法,以下所示。
<div class="weui-cells__title">症状图片</div> <div class="weui-cells weui-cells_form"> <div class="weui-cell"> <div class="weui-cell__bd weui-cell_primary"> <div class="weui-uploader"> <!--预览图片的列表--> <ul class="weui-uploader__files" id="imgSick"> </ul> <div class="weui-uploader__input-box"> <!--上传图片附件控件--> <input id="uploaderSick" class="weui-uploader__input" type="file" accept="image/*" multiple=""> </div> </div> </div> </div> </div>
为了实现选择图片文件的时候,预览图片的列表能够动态变化(动态增长 li 项目),咱们须要定义对应的事件来实现这个操做。
//存放文件图片的集合 var fileSick = new Array(); var filePres = new Array(); function initImage() { var tmpl = '<li class="weui-uploader__file" style="background-image:url(#url#)"></li>', $gallery = $("#gallery"), $galleryImg = $("#galleryImg"), $uploaderSick = $("#uploaderSick"), $imgSick = $("#imgSick"), $uploaderPres = $("#uploaderPres"), $imgPres = $("#imgPres"); //症状图片上传 $uploaderSick.on("change", function (e) { var src, url = window.URL || window.webkitURL || window.mozURL, files = e.target.files; for (var i = 0, len = files.length; i < len; ++i) { var file = files[i]; fileSick.push(file);//加入集合 if (url) { src = url.createObjectURL(file); } else { src = e.target.result; } $imgSick.append($(tmpl.replace('#url#', src))); } }); ..............
咱们注意到了,这里没有使用chooseImage的JSSDK接口,而是经过 url.createObjectURL(file) 的方式获取路径,展现在图片列表控件里面。
对于动态增长的图片,咱们可让它支持单击预览的方式,预览实际上是把图片放在一个预览层里面。
var index; //第几张图片 var category;//那个类别 var imgid;//图片ID //症状图片单击处理 $imgSick.on("click", "li", function() { index = $(this).index(); category = "sick"; imgid = $(this).attr("id"); $galleryImg.attr("style", this.getAttribute("style")); $gallery.fadeIn(100); });
预览层的DIV是放在主界面上的,主界面是一个放置图片的区域,底部是一个删除按钮,用来咱们实现图片删除操做的。
<!--图片预览层--> <div class="weui-gallery" id="gallery"> <span class="weui-gallery__img" id="galleryImg" style="width:auto"></span> <div class="weui-gallery__opr"> <a href="javascript:" class="weui-gallery__del"> <i class="weui-icon-delete weui-icon_gallery-delete"></i> </a> </div> </div>
预览层再次单击的时候关闭,执行的JS代码以下所示。
$gallery.on("click", function() { $gallery.fadeOut(100); });
删除图片的时候,咱们区分是存在服务器的图片,仍是本地临时选择的图片,区别对待。若是服务器图片,须要提示确认删除,若是是本地临时图片,直接移除便可。
//删除图片(根据类别和序号处理) $(".weui-gallery__del").click(function () { console.log(index + ',' + category + ',' + imgid);//记录显示 //若是是在服务端的图片,确认后移除 if (imgid != undefined && imgid != '') { $.confirm("您肯定要永久删除该图片吗?", "永久删除?", function () { var url = "/H5/DeleteAttachment?openid=@ViewBag.openid"; var postData = { id: imgid.replace(/img_/, '') //控件id去掉前缀为真正附件ID }; $.post(url, postData, function (json) { //转义JSON为对象 var data = $.parseJSON(json); if (data.Success) { $.toptip("删除成功!", 'success'); //在界面上找到对应控件ID,移除控件 RemoveImg(); } else { $.toast("操做失败:" + data.ErrorMessage, "forbidden"); } }); }); } else { RemoveImg(); //普通图片快速移除 }; });
其中移除图片显示的JS代码以下所示。
//移除对应类别的图片 function RemoveImg() { if (category == "sick") { $imgSick.find("li").eq(index).remove(); fileSick.splice(index, 1); } else { $imgPres.find("li").eq(index).remove(); filePres.splice(index, 1); } };
咱们要使用表单上传文件的方式,就须要在JS里面建立一个FormData的对象,用来承载文件内容,以下所示
var formData = new FormData();//构建一个FormData存储复杂对象
若是是常规的表单数据,咱们经过键值,把内容填入FormData便可,以下所示。
var formData = new FormData();//构建一个FormData存储复杂对象 formData.append("PatientName", $("#PatientName").val());
若是是图片附件的,咱们则须要遍历集合文件,把它们逐一加入对应键值里面,为了区分不一样的类别文件,咱们使用不一样的前缀方式,以下代码所示。
//加入症状图片 for (var i = 0; i < fileSick.length; i++){ formData.append("sick_" + i, fileSick[i]); }; //加入处方图片 for (var i = 0; i < filePres.length; i++){ formData.append("pres_" + i, filePres[i]); };
//提交表单数据和文件 var url = "/H5/DrugInquirySave2?openid=@ViewBag.openid"; $.ajax({ url: url, type: 'post', processData: false, contentType: false, data: formData, success: function (json) { //转义JSON为对象 var data = $.parseJSON(json); if (data.Success) { $.toast("处方已提交审核中,稍后请处处方查询查看。"); //WeixinJSBridge.call('closeWindow');//关闭窗口 location.href = "/h5/Prescription";//跳转处处方页面 } else { $.toast("保存失败:" + data.ErrorMessage, "forbidden"); } } });
在后台的处理函数 DrugInquirySave2 里面,咱们须要把文件按键名提取出来,根据文件键名的不一样,放到不一样给的集合里面存储起来便可。
以下是DrugInquirySave2 函数里面的部分代码,用来处理收到的表单文件集合。而后咱们在把文件写入文件系统便可,这样省却了对JSSDK提交文件,再去微信服务器提取文件方式的麻烦,直接由客户端把文件上传的本身的文件服务器了。
#region 经过文件附件方式获取 var files = Request.Files; if (files != null && files.Count > 0) { LogTextHelper.Info(string.Format("收到文件:{0}", files.Count));//测试 foreach (string key in files.Keys) { LogTextHelper.Info(string.Format("收到文件key:{0}", key)); var fileData = files[key]; bool isSickImage = key.ToLower().IndexOf("sick") >= 0;//判断是否为问诊图片分类 if (fileData != null) { HttpContext.Request.ContentEncoding = Encoding.GetEncoding("UTF-8"); HttpContext.Response.ContentEncoding = Encoding.GetEncoding("UTF-8"); HttpContext.Response.Charset = "UTF-8"; string fileName = Path.GetFileName(fileData.FileName); //原始文件名称 string fileExtension = Path.GetExtension(fileName); //文件扩展名 FileUploadInfo fileInfo = new FileUploadInfo(); fileInfo.FileData = ReadFileBytes(fileData); if (fileInfo.FileData != null) { fileInfo.FileSize = fileInfo.FileData.Length; } //判断图片类别分组 fileInfo.Category = isSickImage ? "问诊图片" : "处方图片"; fileInfo.FileName = fileName; fileInfo.FileExtend = fileExtension; //判断属于那个分组【这里只有两个分组】 fileInfo.AttachmentGUID = isSickImage ? SickAttachGUID : PresAttachGUID; fileInfo.AddTime = DateTime.Now;//建立时间 fileInfo.Editor = openid;//记录人 fileInfo.Owner_ID = info.ID;//属于主表记录 result = BLLFactory<FileUpload>.Instance.Upload(fileInfo); if (!result.Success) { LogTextHelper.Error("上传文件失败:" + result.ErrorMessage); } } } } #endregion
编辑现有记录的时候,也能够实现对已有图片的删除操做,临时文件的预览处理和再次上传等操做。
本篇随笔是基于公众号上传图片文件的两种方式的处理,分别是使用微信JSSDK和使用URL.createObjectURL上传预览图片的不一样处理对比,两种方式都可以知足图片的处理操做。对比处理代码,可能使用后者可能更加简洁一些。并且微信浏览器对URL.createObjectURL的支持也很是不错,能够在微信开发工具和实际环境上都正常使用。