因公司項目須要,要修改一個手機端上傳圖片的一個功能,本來的項目用的是input 的file控件上傳的,雖然標註了能夠多選,可是在實際運用當中只有iOS手機能夠實現多選,Android手機並不支持多選,甚至部分Android手機由於input file 上multiple="multiple"屬性連圖片選擇框都打開不了。而此次的需求須要用戶上傳多張圖片,顯然讓用戶一張圖片一張圖片上傳知足不了需求,咱們須要多選上傳。html
由於咱們的項目是掛在微信企業號裏面的,因此我想到用微信企業號的JS-SDK的方法來選擇圖片,能夠直接繞開file控件的缺陷。直接上代碼: 前端
wx.chooseImage({ count: 8, // 默認9 sizeType: ['original', 'compressed'], // 能夠指定是原圖仍是壓縮圖,默認兩者都有 sourceType: ['album', 'camera'], // 能夠指定來源是相冊仍是相機,默認兩者都有 success: function (res) { $(".previewimg").html("");//每次選擇圖片完成後都清空以前已經添加的html節點 var images = { localId: [],//微信返回的本地id列表 serverId: [],//微信返回的服務器id列表 }; ioslocId = [];//用於兼容ios的本地id列表 圖片是base64格式的 rows = "";//聲明一個空字符串用於保存循環出來的html images.localId = images.localId.concat(res.localIds); // 返回選定照片的本地ID列表,localId能夠做爲img標籤的src屬性顯示圖片 localId = images.localId; try { for (var i = 0; i < images.localId.length; i++) { //alert(res.localIds[i]); wx.getLocalImgData({ //循環調用 getLocalImgData localId: res.localIds[i], // 圖片的localID success: function (res) { var localData = res.localData; // localData是圖片的base64數據,能夠用img標籤顯示 //alert(localData); if (window.__wxjs_is_wkwebview) { //ios localData = localData.replace('jgp', 'jpeg');//iOS 系統裏面獲得的數據,類型爲 image/jgp,所以須要替換一下 } else { localData = "data:image/jpeg;base64," + localData; //android } ioslocId.push(localData) //把base64格式的圖片添加到ioslocId數組裏 這樣該數組裏的元素都是base64格式的 rows = ""; for (var j = 0; j < ioslocId.length; j++) { rows += '<img style="margin: 5px;" class="up_p" src="' + ioslocId[j] + '"> <input type="hidden" name="upimg' + j + '" value="' + ioslocId[j] + '" />'; } $(".previewimg").html(rows); }, fail: function (e) { alert(e); } }); } } catch (err) { alert(err) // 可執行 } } });
這裏注意到:微信企業號api官網上:https://work.weixin.qq.com/api/doc#10029/獲取本地圖片接口android
說:wx.getLocalImgData,此接口僅在企業微信iOS支持,要求版本爲2.4.6及以上,可是咱們很顯然的發現,Android也能夠用只是返回的res.localData作處理的不一樣而已。ios
一開始我也不知道安卓手機獲取的圖片能夠直接用getLocalImgData的返回值轉base64,因此我想了不少其餘辦法,也走了很多冤枉路,好比我想用前端顯示的圖片轉base64,用canvas ,以下:web
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/png"); return dataURL // return dataURL.replace("data:image/png;base64,", ""); }
可是很遺憾,這裏的img必須是同一域裏的圖片或者是容許跨域訪問的圖片地址,而微信返回的是本地ID,即便安卓手機上這個id能夠直接放在img 的src上顯示圖片,但不知是由於id問題仍是由於這個「地址」不容許跨域訪問致使用canvas轉的base64是錯的,傳到服務器端後不能保存成咱們選擇的圖片。因此這個方法確定不行。canvas
因而通過他人的指點我想到用wx.uploadImage接口先上傳到微信服務器,而後再用微信提供的接口在服務器端將微信服務器的圖片下載到咱們本身的服務器。c#
wx.uploadImage({ localId: '', // 須要上傳的圖片的本地ID,由chooseImage接口得到 isShowProgressTips: 1, // 默認爲1,顯示進度提示 success: function (res) { var serverId = res.serverId; // 返回圖片的服務器端ID } });
服務器端請求地址:https://qyapi.weixin.qq.com/cgi-bin/media/get?access_token=ACCESS_TOKEN&media_id=MEDIA_IDapi
此處得到的 media_id即serverId 。跨域
這裏上傳到微信服務器必須一提的是:上傳圖片只能一張一張圖片上傳,上傳下一張圖片必須等上一張圖片上傳完成,再進行下一張圖片,因此我當即想到寫個遞歸上傳便可,以下:數組
//遞歸上傳到微信服務器 add by torres cooooooooooooooool localId: []; function uploadImg(index) { if (index < 0) { return; } wx.uploadImage({ localId: localId[index], // 須要上傳的圖片的本地ID,由chooseImage接口得到 isShowProgressTips: 1, // 默認爲1,顯示進度提示 success: function (res) { //res.serverId 返回圖片的服務器端ID try { var rows = '<input type="hidden" name="serverIdArr' + index + '" value="' + res.serverId + '" />'; $(".previewimg").append(rows); } catch (e) { alert(e) } uploadImg(index - 1); }, fail: function (res) { alert("上傳失敗,msg:" + JSON.stringify(res)); } }); }
這樣的寫法可能有點low,我在網上看到另外一種
1 var syncUpload = function (localIds, index) { 2 var localId = localIds.pop(); 3 wx.uploadImage({ 4 localId: localId, 5 isShowProgressTips: 1, 6 success: function (res) { 7 //其餘對serverId作處理的代碼 8 try { 9 //alert("上傳下一張"); 10 var serverId = res.serverId; // 返回圖片的服務器端ID 11 var rows = '<input type="hidden" name="serverIdArr' + index + '" value="' + res.serverId + '" />'; 12 $(".previewimg").append(rows); 13 if (localIds.length > 0) { 14 syncUpload(localIds, index - 1); 15 } 16 } catch (e) { 17 alert(e) 18 } 19 }, fail: function (res) { 20 alert("上傳失敗,msg:" + JSON.stringify(res)); 21 } 22 }); 23 };
兩種方法差很少,都能實現我想要的,可是僅僅是在iOS。很奇怪,在Android機器上,上傳多張圖片時並不能上傳所有圖片並返回sercerId並打印出input隱藏域在頁面上(或者說並非全部圖片上傳都走了success方法,但也沒有走fail方法),單張圖片上傳徹底OJBK。這就很讓人鬱悶了,選擇了三張圖片上傳微信服務器,可是隻返回了一個或兩個serverId,十分不穩定,有時候能所有上傳,有時候只能上傳部分圖片。
這裏也順便貼出C#語言後臺下載微信服務器端圖片代碼:
string stUrl = string.Format("https://qyapi.weixin.qq.com/cgi-bin/media/get?access_token={0}&media_id={1}", access_token, serverIdArr[i]); HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create(stUrl); HttpWebResponse myResponse = (HttpWebResponse)req.GetResponse(); string contentType = myResponse.ContentType; if (myResponse.ContentLength == 0 || (contentType != "image/jpeg" && contentType != "image/pjpeg" && contentType != "image/bmp" && contentType != "image/gif" && contentType != "image/png" && contentType != "image/x-png")) { htInfo["error"] += "第" + (i + 1) + "個文件上傳的附件格式不對,請從新上傳!<br/>"; continue; } int picUploadSize = Convert.ToInt32(ConfigurationManager.AppSettings["picUploadSize"]); if (myResponse.ContentLength > picUploadSize * 1024) { htInfo["error"] += string.Format("第" + (i + 1) + "個圖片不能超過{0}KB!<br/>", picUploadSize); continue; } req.Method = "GET"; //req.ProtocolVersion = HttpVersion.Version10; string strpath = myResponse.ResponseUri.ToString(); WebClient mywebclient = new WebClient(); string extension = GetExtensionName(contentType); //獲取擴展名 string imgName = DateTime.Now.ToString("yyyyMMddHHmmssffff") + extension; //下載原圖 mywebclient.DownloadFile(strpath, savepath);
最終由於Android上傳圖片的問題這個方法仍是行不通。沒有辦法之下我只能在 wx.chooseImage的success方法裏顯示選擇圖片的下面再加一個上傳按鈕,讓用戶手動一個一個點擊上傳到微信服務器,這樣保證了每張均可以返回serverId,這麼low的方法只能先用着了,正當我要放棄繼續探索其餘方法的時候,我在微信開發羣裏面問了一下我這個問題,而後有好心的羣友給了我建議說直接轉base64,不用上傳微信服務器時我就告訴了他個人疑問,他給了我他的代碼,最終有了
localData = "data:image/jpeg;base64," + localData; //android上面這行代碼 ,苦苦追尋,在埋頭研究兩天後終於實現了手機端選擇多圖上傳的功能,在用戶操做上也不用區分iOS和Android。以上的故事告訴咱們:不要輕言放棄,這種方法不行就換一種再試試,你會發現,哪種方式都TM不行啊!!!我屮艸芔茻。略略略略略 ,纔不是呢!你會發現總有一種適合你。有什麼建議或意見能夠請你們多多指點,謝謝!