记一次开发微信公众号时怎样利用微信JS接口(简称:WX-JS)进行图片上传
注:本项目前端基于angular框架,后台nodejs
后台相关技术点:获取WX-JS权限,获取accesstoken,从微信后台下载多媒体文件,上传文件
前端相关技术点:调用js接口、预览选择的图片
1、绑定域名(具体操作请移步:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141115)
2、引入js文件
在页面引入js:
<script src="http://res.wx.qq.com/open/js/jweixin-1.2.0.js"></script>
3、获取WX-JS权限配置(成功之后才能调用相关接口),获取accesstoken(上传图片的时候需要把accesstoken传到后台才能从微信后台下载多媒体文件)
新建一个WXService服务,在服务里写入相关方法:获取WX-JS权限:initWX()、获取accesstoken:getaccessToken()、拍照、本地选图:chooseImage()、上传图片:uploadImage()、获取本地图片接口:getLocalImgData()
调用后台api: /mall/checkjsapi、/mall/getaccesstoken
//微信接口服务
.factory('WXService', ["$rootScope", "$http", "$window", "zxhttp", "$location",
function($rootScope, $http, $window, zxhttp,$location) {
var wxData = {};
var position = {};
return {
initWX: function() {
console.log("获取WX-JS权限...");
$http({
method:"get",
params:{appid: $rootScope.appid},
url:"/mall/checkjsapi"
}).success(function(data){
console.log(data
if(data.DataJson[0].ccode==200){
wxData = data.DataJson[0];
wx.config({
debug: false, // 开启调试模式
appId: wxData.appId, // 必填,公众号的唯一标识
timestamp: wxData.timestamp, // 必填,生成签名的时间戳
nonceStr: wxData.nonceStr, // 必填,生成签名的随机串
signature: wxData.signature, // 必填,签名,见附录1
jsApiList: ['openLocation', 'getLocation', 'scanQRCode','chooseImage', 'previewImage','uploadImage','downloadImage','getLocalImgData','onMenuShareAppMessage' ]//必填,需要使用的JS接口列表,所有JS接口列表见附录2
});
}
else{
console.log("获取WX-JS权限失败!")
}
}).error(function(err){
console.log("获取WX-JS权限失败!")
console.log(error)
})
},
//获取accesstoken
getaccessToken: function(callback) {
console.log("获取accessToken...");
$http({
method:"get",
params:{appid: $rootScope.appid},
url:"/mall/getaccesstoken"
}).success(function(data){
console.log(data)
if(data.Result==200){
callback(data.DataJson[0].access_token)
}
else{
callback(null)
console.log("获取accessToken失败!")
}
}).error(function(err){
callback(null)
console.log("获取accessToken失败!")
console.log(err)
})
},
//拍照、本地选图
chooseImage: function(type,num,callback) {
console.log(num)
var count=5-num
console.log(count)
wx.chooseImage({
count:count,//默认9张
sizeType: ['compressed'], // ['original', 'compressed']可以指定是原图还是压缩图,默认二者都有
sourceType: [type], // ['camera','album']可以指定来源是相册还是相机,默认二者都有
success: function(res) {
var images = res.localIds;
console.log(res)
callback(images);
}
});
},
//上传图片
uploadImage: function(id,callback){
wx.uploadImage({
localId: id.toString(), // 需要上传的图片的本地ID,由chooseImage接口获得
isShowProgressTips: 1, // 默认为1,显示进度提示
success: function (res) {
// var serverId = res.serverId; // 返回图片的服务器端ID
callback(res)
}
})
},
//获取本地图片接口
getLocalImgData:function(localID,callback){
wx.getLocalImgData({
localId: localID, // 图片的localID
success: function (res) {
var localData = res.localData; // localData是图片的base64数据,可以用img标签显示
if (localData.indexOf('data:image') == -1) { //安卓系统得到的数据,是没有 data:image/jpeg;base64, 前缀的.
localData = localID;
//localData ='data:image/jpeg;base64,'+localData
}
if(localData.substring(0,5)=='data:'){
localData = localData.replace('jgp','jpeg');//iOS 系统里面得到的数据,类型为 image/jgp,因此需要替换一下
}
console.log("读取图片")
callback(localData)
}
})
}
}
}
])
4、在控制器里调用服务
控制器注入WXService服务,调用初始化方法 WXService.initWX();
//初始化
WXService.initWX();
$scope.chooseimgs=[]
$scope.chooseimg={}
$scope.imgs = {};//用于存放页面显示的图片
//弹出框拍照或从相册中选择
$scope.showActionSheet=function(){
if($scope.chooseimgs.length>=5){
console.log("最多只能上传五张图片");
return false
}
$ionicActionSheet.show({
buttons:[{text:'拍照'},{text:'从相册中选择'}],
cancelText: '取消',
cancel: function() {
},
buttonClicked: function(index) {
if(index==0){
$scope.chooseImage('camera',$scope.chooseimgs.length)
return true;
}
if(index==1){
$scope.chooseImage('album',$scope.chooseimgs.length)
return true;
}
}
})
}
//选择图片
$scope.chooseImage=function(type,count){
WXService.chooseImage(type,count,function(res){
if(res.length<=0){
return
}
for(var i=0;i<res.length;i++) {
$scope.guid = (new Date()).valueOf(); //通过时间戳创建一个随机数,作为键名使用
$scope.chooseimg={"localId":res[i],"name":$scope.guid}
$scope.chooseimgs.push($scope.chooseimg)
WXService.getLocalImgData(res[i],function (localData) {
$scope.guid = (new Date()).valueOf(); //通过时间戳创建一个随机数,作为键名使用
$scope.imgs[$scope.guid] = {
imgSrc: localData //接收base64
}
})
}
})
}
$scope.detailphotos=[];
$scope.upload=function(){
var checked=$scope.checkinfo()
if(checked){
if($scope.chooseimgs.length>0){
WXService.getaccessToken(function(data){
if(data){
//console.log(data)
var uploadingCount = 0;
function uploadimg() {
WXService.uploadImage($scope.chooseimgs[uploadingCount].localId,function(res){
var serverId = res.serverId;
$scope.guid = (new Date()).valueOf();
$http({
method: 'GET',
url: 'http://191.111.1/weixinimg?file='+serverId+'&name='+$scope.guid+'&type=evaluate&accesstoken='+data,
headers: {'Content-Type':'application/json'},
}).success(function(result) {
console.log(result)
if (result.code == 200) {
//Message.Message($scope.detailItems.length,1000)
var detailphoto={"img":result.data}
$scope.detailphotos.push(detailphoto);
uploadingCount++;
if(uploadingCount<$scope.chooseimgs.length){
setTimeout(uploadimg(),100)
}
if(uploadingCount>=$scope.chooseimgs.length){
setTimeout(function(){
$scope.handInEvaluate();
},100)
}
}else{
//info=false;
console.log(result);
return false
}
});
})
}
//.uploadImage在chooseImage的回调中有时候Android会不执行,Android6.2会解决此问题,若需支持低版本可以把调用uploadImage放在setTimeout中延迟100ms解决
setTimeout(uploadimg(),100)
}
else{
console.log("获取accessToken失败");
}
})
}else {
$scope.handInEvaluate();
}
}
}
//删除图片
$scope.delImg = function (item) {
$ionicActionSheet.show({
buttons: [{text: '删除'}],
cancelText: '取消',
cancel: function () {
},
buttonClicked: function () {
for(var key in $scope.imgs){
if($scope.imgs[key].imgSrc == item){
delete $scope.imgs[key];
$scope.chooseimgs.splice($scope.chooseimgs.indexOf(item),1);
return true;
}
}
}
})
}
5、页面显示
<!-- 添加图片按钮 -->
<ion-item>
<div style="width: 100%;">
<div ng-repeat="img in imgs" ng-click="showimg(img.imgSrc)">
<div style="margin:1%;width: 31%;height:100px;float:left;background-image: url('{{img.imgSrc}}');background-size:cover;background-position: center center;background-repeat: no-repeat" on-hold="delImg(img.imgSrc)"></div>
</div>
</div>
<button class="button button-outline button-stable" ng-click="showActionSheet()" style="float:left;margin:2%;width: 100px;height:100px;">
<div class="weui_grid_icon">
<i class="icon ion-images"></i>
</div>
<p class="weui_grid_label">添加图片</p>
</button>
</ion-item>
6、后台(node)
需安装wechat-api模块(https://www.npmjs.com/package/wechat-api )、redis缓存
获取获取WX-JS权限的api:/mall/checkjsapi
获取accessToken的api: /mall/getaccesstoken
var API = require('wechat-api');
var redisClient = require('../apis/config/redis_database').redisClient;
var config = require('../config/config').config;//公众的配置文件:appid,开发密码secret,域名domai等等
var api = new API(config.appid, config.secret);
//微信JS-SDK权限授权
function checkjsapi (req,callback){
console.log("-------------------- checkJsApi ----------------------");
api.getTicket(function(err,result){
// console.log(result);
if (err) {
callback(err);
}
else{
var param = {
debug: config.debug, //是否开启调试模式
jsApiList: config.jsapilist, //js-sdk的权限数组
url: "http://"+config.domain+'/webpage/index.html' //微信后台认证的域名
};
api.getJsConfig(param, function(err,data){
//console.log(data);
var jsonData = {
// ticket : result.ticket,
appId : config.appid, //微信公众号的appid
timestamp : data.timestamp,
nonceStr : data.nonceStr,
signature : data.signature
};
console.log("jsonData",jsonData);
callback(jsonData)
});
}
});
};
function getaccessToken(appid,callback){
console.log("********getaccessToken*********")
var name=appid+"accesstoken"
redisClient.get(name,function(err,reply){
if(err){
console.log("Redis系统出错",err);
callback(err)
}
if(reply==null){
api.getAccessToken(function(err,result){
if(err){
console.log("err22:",err)
callback(null,err);
}else{
console.log("result11:",result)
//保存AccessToken
redisClient.set(appid+"accesstoken", result.accessToken);
redisClient.expire(appid+"accesstoken", result.expireTime);//60一分钟内有效
callback(result);
}
})
}
else{
console.log("获取保存的accesstoken:"+reply)
callback({"access_token":reply})
}
})
}
7、上传图片api: /weixinimg
微信公众号上传图片,微信系统会把图片上传到微信服务器,我们需要调用微信获取多媒体的接口把图片下载到要上传的文件夹里(接口详解:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1444738727)
var request = require('request');
var fs = require('fs');
function weixinimg(callback) {
return function(req,res){
res.setHeader("Access-Control-Allow-Origin", "*");
res.setHeader("Access-Control-Allow-Methods","GET");
res.setHeader("Access-Control-Allow-Credentials",true);
res.setHeader("Access-Control-Allow-Headers","x-requested-with,content-type,accept,token,client,Origin,content,openid");
//res.setHeader("Content-Type","image/jpeg");
console.log("*************上传图片****************")
//var api = new API();
var type=req.query.type;//上传到哪个文件夹
var extName='';
var imgname=req.query.name
var accesstoken=req.query.accesstoken
var path=type+'/'
var url ='http://api.weixin.qq.com/cgi-bin/media/get?access_token='+accesstoken+'&media_id='+req.query.file;//获取上传的图片的media_id
fs.exists(path,function(exists){
if(!exists){
fs.mkdir(path,0777,function(err){
if(err){
console.log('路徑'+path+'创建失败!')
res.send({
code: 202,
msg: '路徑'+path+'创建失败!',
data:''
});
}
});
}
})
var reqhttp=request.get(url)
reqhttp.on('response',function(ress){
//console.log(ress.statusCode) // 200
//console.log(ress.headers['content-type']) // 'image/png'
if(ress.statusCode=='200'){
//console.log("响应请求成功")
if(ress.headers['content-type']!='image/jpeg'){
console.log("响应请求不成功",ress.headers['content-type'])
res.send({code:202,msg:'图片响应请求不成功'})
return false
}
switch (ress.headers['content-type']) {
case 'image/pjpeg':
extName = '.jpg';
break;
case 'image/jpeg':
extName = '.jpg';
break;
case 'image/png':
extName = '.png';
break;
case 'image/x-png':
extName = '.png';
break;
}
if (extName.length === 0) {
console.log('只支持png和jpg格式图片')
res.send({code:202,msg:'只支持png和jpg格式图片'})
return false
}
var path1=type+"/"
var paths=path+imgname+extName
var WriteStream=fs.createWriteStream(paths)
reqhttp.pipe(WriteStream);
WriteStream.on("finish",function(){
console.log('图片上传成功')
res.send({code:200,msg:'图片上传成功',data:path+imgname+extName})
})
WriteStream.on("error",function(){
console.log('图片上传出错')
res.send({code:202,msg:'图片上传失败'})
})
}else{
console.log("响应请求不成功statusCode:",ress.statusCode)
res.send({code:202,msg:'图片响应请求不成功'})
}
})
reqhttp.on('error',function(err){
console.log('获取微信服务器图片出错:'+err)
res.send({code:202,msg:'获取微信服务器图片出错',data:err})
})
}
};
最后的页面显示结果如下图: