因公司项目须要,要修改一个手机端上传图片的一个功能,本来的项目用的是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不行啊!!!我屮艸芔茻。略略略略略 ,才不是呢!你会发现总有一种适合你。有什么建议或意见能够请你们多多指点,谢谢!