在作微信公衆號或者企業微信開發業務應用的時候,咱們經常會涉及到圖片預覽、上傳等的處理,每每業務需求不止一張圖片,所以相對來講,須要考慮的全面一些,用戶還須要對圖片進行預覽和相應的處理,在開始的時候我使用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的支持也很是不錯,能夠在微信開發工具和實際環境上都正常使用。