2017-01-16 15:47:38 u014783753 阅读数 2983
  • 微信支付开发-微信公众号开发12-微信开发php

    微信公众平台开发之微信支付开发是子恒老师《微信公众平台开发》视频教程的第12部。详细讲解了用php进行微信支付的开发。内容包含获取支付密钥,微信公众号支付开发,扫码支付,微信刷卡支付,异步处理支付结果等等。欢迎反馈,微信/QQ:68183131

    27864 人正在学习 去看看 秦子恒



微信页面分享到朋友圈、分享朋友等等,默认的是有分享格式的,比如分享给朋友,默认的图标是页面的第一张图片,内容是该页面的url,这个显然有些场景是不满足的。对于挑剔的产品经理一定是会有自己的设计的,比如图片、内容等。

微信js-sdk提供了关于这部分的接口,现将过程说明如下。

文档路径,在登录公众号后路径如下:点击打开链接,在左侧“微信网页开发”--“微信JS-SDK说明文档”菜单栏中

点击“3.2 获取“分享给朋友”按钮点击状态及自定义分享内容接口”我们可以看到如下内容:

wx.onMenuShareAppMessage({
    title: '', // 分享标题
    desc: '', // 分享描述
    link: '', // 分享链接
    imgUrl: '', // 分享图标
    type: '', // 分享类型,music、video或link,不填默认为link
    dataUrl: '', // 如果type是music或video,则要提供数据链接,默认为空
    success: function () { 
        // 用户确认分享后执行的回调函数
    },
    cancel: function () { 
        // 用户取消分享后执行的回调函数
    }
});
在以上代码片段中我们看到了我们可以定义“微信分享视图”中的这些项:标题、描述、连接、图片以及类型。为了举例,我们只说标题和描述。

看了以上代码段,问题来了,wx这个对象我们怎么定义以及怎么配置呢?
在改说明文档下的步骤一、步骤二和步骤三说明了我们怎么使用wx这个对象以及怎么配置,如下:

JSSDK使用步骤
步骤一:绑定域名
先登录微信公众平台进入“公众号设置”的“功能设置”里填写“JS接口安全域名”。
备注:登录后可在“开发者中心”查看对应的接口权限。
步骤二:引入JS文件
在需要调用JS接口的页面引入如下JS文件,(支持https):http://res.wx.qq.com/open/js/jweixin-1.0.0.js
如需使用摇一摇周边功能,请引入 http://res.wx.qq.com/open/js/jweixin-1.1.0.js
备注:支持使用 AMD/CMD 标准模块加载方法加载
步骤三:通过config接口注入权限验证配置
所有需要使用JS-SDK的页面必须先注入配置信息,否则将无法调用(同一个url仅需调用一次,对于变化url的SPA的web app可在每次url变化时进行调用,目前Android微信客户端不支持pushState的H5新特性,所以使用pushState来实现web app的页面会导致签名失败,此问题会在Android6.2中修复)。
wx.config({
    debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
    appId: '', // 必填,公众号的唯一标识
    timestamp: , // 必填,生成签名的时间戳
    nonceStr: '', // 必填,生成签名的随机串
    signature: '',// 必填,签名,见附录1
    jsApiList: [] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2
});

以上代码段我们需要注意wx.config({})中定义的参数:

debug我们不再赘述

appId,就是公众号的Id

timestamp,暂时看就是一个时间戳文本

nonceStr,暂时看就是一个随机串,

jsApiList,需要使用的js接口列表,这个看上去也没有什么问题,应该是js接口列表中列出的接口的一个array

需要我们注意的是signature以及signature和timestamp和nonceStr的关系。根据以上代码段列出的备注,我们看附录一。

附录一描述如下

附录1-JS-SDK使用权限签名算法
jsapi_ticket
生成签名之前必须先了解一下jsapi_ticket,jsapi_ticket是公众号用于调用微信JS接口的临时票据。正常情况下,jsapi_ticket的有效期为7200秒,通过access_token来获取。由于获取jsapi_ticket的api调用次数非常有限,频繁刷新jsapi_ticket会导致api调用受限,影响自身业务,开发者必须在自己的服务全局缓存jsapi_ticket 。
1.参考以下文档获取access_token(有效期7200秒,开发者必须在自己的服务全局缓存access_token):../15/54ce45d8d30b6bf6758f68d2e95bc627.html
2.用第一步拿到的access_token 采用http GET方式请求获得jsapi_ticket(有效期7200秒,开发者必须在自己的服务全局缓存jsapi_ticket):https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi
成功返回如下JSON:
{
"errcode":0,
"errmsg":"ok",
"ticket":"bxLdikRXVbTPdHSM05e5u5sUoXNKd8-41ZO3MhKoyN5OfkWITDGgnr2fwJ0m9E8NYzWKVZvdVtaUgWvsdshFKA",
"expires_in":7200
}
获得jsapi_ticket之后,就可以生成JS-SDK权限验证的签名了。
通过阅读以上代码段说明的文本,我们可以看出来,要想生成signature这个参数,不许要生成ticket这个“票据”。

需要我们注意的是,生成这个“票据”又需要我们获得access_token,根据以上../15/54....这段说明我们看下怎么生成access_token这个参数。

说明如下:

获取access token
access_token是公众号的全局唯一票据,公众号调用各接口时都需使用access_token。开发者需要进行妥善保存。access_token的存储至少要保留512个字符空间。access_token的有效期目前为2个小时,需定时刷新,重复获取将导致上次获取的access_token失效。

公众平台的API调用所需的access_token的使用及生成方式说明:

1、为了保密appsecrect,第三方需要一个access_token获取和刷新的中控服务器。而其他业务逻辑服务器所使用的access_token均来自于该中控服务器,不应该各自去刷新,否则会造成access_token覆盖而影响业务;
2、目前access_token的有效期通过返回的expire_in来传达,目前是7200秒之内的值。中控服务器需要根据这个有效时间提前去刷新新access_token。在刷新过程中,中控服务器对外输出的依然是老access_token,此时公众平台后台会保证在刷新短时间内,新老access_token都可用,这保证了第三方业务的平滑过渡;
3、access_token的有效时间可能会在未来有调整,所以中控服务器不仅需要内部定时主动刷新,还需要提供被动刷新access_token的接口,这样便于业务服务器在API调用获知access_token已超时的情况下,可以触发access_token的刷新流程。
如果第三方不使用中控服务器,而是选择各个业务逻辑点各自去刷新access_token,那么就可能会产生冲突,导致服务不稳定。

公众号可以使用AppID和AppSecret调用本接口来获取access_token。AppID和AppSecret可在微信公众平台官网-开发者中心页中获得(需要已经成为开发者,且帐号没有异常状态)。注意调用所有微信接口时均需使用https协议。

接口调用请求说明

http请求方式: GET
https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
参数说明

参数	是否必须	说明
grant_type	是	获取access_token填写client_credential
appid	是	第三方用户唯一凭证
secret	是	第三方用户唯一凭证密钥,即appsecret
返回说明

正常情况下,微信会返回下述JSON数据包给公众号:

{"access_token":"ACCESS_TOKEN","expires_in":7200}
参数	说明
access_token	获取到的凭证
expires_in	凭证有效时间,单位:秒

错误时微信会返回错误码等信息,JSON数据包示例如下(该示例为AppID无效错误):

{"errcode":40013,"errmsg":"invalid appid"}
通过以上代码段列出的文本我们很快可以看到,只要传入appid和appsecret以及grant_type这三个参数给列出的url既可以获得返回的access_token这个参数,这里我们不再赘述。

需要注意的是我在处理这块儿的时候想当然的使用oauth认证的access_token来获取“票据”,最后证明返回一直是invalid,即参数错误是不行的。不行的方式如下:

Get:
https://api.weixin.qq.com/sns/oauth2/access_token
传入:
appid
secretid
以及code
其中code是微信回调方法传入的,这里我们不再赘述
事实证明,这种方式获得access_token不能获得“票据”

必须用以上的以上代码段描述的方式获得。谢谢。

根据文档描述,在获得代码段后我们就可以生成“签名了”,其中签名的算法如下:

签名算法
签名生成规则如下:参与签名的字段包括noncestr(随机字符串), 有效的jsapi_ticket, timestamp(时间戳), url(当前网页的URL,不包含#及其后面部分) 。对所有待签名参数按照字段名的ASCII 码从小到大排序(字典序)后,使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串string1。这里需要注意的是所有参数名均为小写字符。对string1作sha1加密,字段名和字段值都采用原始值,不进行URL 转义。
即signature=sha1(string1)。 示例:
noncestr=Wm3WZYTPz0wzccnW
jsapi_ticket=sM4AOVdWfPE4DxkXGEs8VMCPGGVi4C3VM0P37wVUCFvkVAy_90u5h9nbSlYy3-Sl-HhTdfl2fzFy1AOcHKP7qg
timestamp=1414587457
url=http://mp.weixin.qq.com?params=value
步骤1. 对所有待签名参数按照字段名的ASCII 码从小到大排序(字典序)后,使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串string1:
jsapi_ticket=sM4AOVdWfPE4DxkXGEs8VMCPGGVi4C3VM0P37wVUCFvkVAy_90u5h9nbSlYy3-Sl-HhTdfl2fzFy1AOcHKP7qg&noncestr=Wm3WZYTPz0wzccnW×tamp=1414587457&url=http://mp.weixin.qq.com?params=value
步骤2. 对string1进行sha1签名,得到signature:
0f9de62fce790f9a083d5c99e95740ceb90c27ed
以上文本不难理解,认真读取后按照方法做就能获得相应的签名。

需要注意的是:

注意事项

1.签名用的noncestr和timestamp必须与wx.config中的nonceStr和timestamp相同。

2.签名用的url必须是调用JS接口页面的完整URL。

3.出于安全考虑,开发者必须在服务器端实现签名的逻辑。

以上的1步骤也就回答了signature和noncestr和timestamp的关系。

至此我们就能争取的配置wx.config,不出意外的情况下,调用“发送给朋友”的接口就能个性化配置分享的试图了。


这里附上后端生成signature的java实现

import java.util.UUID;
import java.util.Map;
import java.util.HashMap;
import java.util.Formatter;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.io.UnsupportedEncodingException;  

class Sign {
    public static void main(String[] args) {
        String jsapi_ticket = "jsapi_ticket";

        // 注意 URL 一定要动态获取,不能 hardcode
        String url = "http://example.com";
        Map<String, String> ret = sign(jsapi_ticket, url);
        for (Map.Entry entry : ret.entrySet()) {
            System.out.println(entry.getKey() + ", " + entry.getValue());
        }
    };

    public static Map<String, String> sign(String jsapi_ticket, String url) {
        Map<String, String> ret = new HashMap<String, String>();
        String nonce_str = create_nonce_str();
        String timestamp = create_timestamp();
        String string1;
        String signature = "";

        //注意这里参数名必须全部小写,且必须有序
        string1 = "jsapi_ticket=" + jsapi_ticket +
                  "&noncestr=" + nonce_str +
                  "×tamp=" + timestamp +
                  "&url=" + url;
        System.out.println(string1);

        try
        {
            MessageDigest crypt = MessageDigest.getInstance("SHA-1");
            crypt.reset();
            crypt.update(string1.getBytes("UTF-8"));
            signature = byteToHex(crypt.digest());
        }
        catch (NoSuchAlgorithmException e)
        {
            e.printStackTrace();
        }
        catch (UnsupportedEncodingException e)
        {
            e.printStackTrace();
        }

        ret.put("url", url);
        ret.put("jsapi_ticket", jsapi_ticket);
        ret.put("nonceStr", nonce_str);
        ret.put("timestamp", timestamp);
        ret.put("signature", signature);

        return ret;
    }

    private static String byteToHex(final byte[] hash) {
        Formatter formatter = new Formatter();
        for (byte b : hash)
        {
            formatter.format("%02x", b);
        }
        String result = formatter.toString();
        formatter.close();
        return result;
    }

    private static String create_nonce_str() {
        return UUID.randomUUID().toString();
    }

    private static String create_timestamp() {
        return Long.toString(System.currentTimeMillis() / 1000);
    }
}





2018-06-22 11:11:36 qq_36500554 阅读数 1294
  • 微信支付开发-微信公众号开发12-微信开发php

    微信公众平台开发之微信支付开发是子恒老师《微信公众平台开发》视频教程的第12部。详细讲解了用php进行微信支付的开发。内容包含获取支付密钥,微信公众号支付开发,扫码支付,微信刷卡支付,异步处理支付结果等等。欢迎反馈,微信/QQ:68183131

    27864 人正在学习 去看看 秦子恒

      今天整理一下微信开发中遇到的图片和附件的上传问题。

      开发环境参考:微信开发(一)--分享接口 点击打开链接

      由于是基于weixin-java-tools 封装的java sdk,所以在微信底层处理上我们可以直接调用WxMpService.

      .数据准备:

           进入页面前,根据条件查询相应的图片附件列表(已有数据时进行展示,没有数据可以忽略):

	           //查询图片数据  
		List<FileModel> imgList = fileModelService.loadFileModelsByBFTT(11);
		   //查询附件数据
		List<FileModel> attachmentFileList = fileModelService.loadFileModelsByBFTT(11);
		

      二.页面准备:    

            jspye页面设置上传按钮,遍历读取图片列表   

    <!-- 图片上传 -->   
				 <div class="form-group">
				    <div class="input-group">
				       <div class="input-group-addon "><span style="color:red;"> </span>图片上传:</div>
				       <input type="hidden" name="fileIds" id="fileIds">
				       <div class="input-group-addon">
				       	
				       				<a href="javascript:void(0)" onclick="chooseImage('${basePath }','${id}');">
						       			<span class="glyphicon glyphicon-plus"></span>
						       		</a>
				       
				       </div>
				    </div>
				  </div>
				  
				  <!-- 缩略图 -->
				  <ul class="body-img-ul">
				  		<c:forEach items="${imgList}" var="v" varStatus="sta">
				  			<li style="background-image:url('${simplePath }${v.revealpath }')" 
				  				onclick="previewImage('${simplePath }${v.revealpath }');">
				  		
					       		<i class="del_icon" onclick="deleteImage('${v.id }',this,'${basePath }')"></i>
		    	            </li>
				  		</c:forEach>
				  </ul>
				   <!-- 附件上传 --> 
				   <div class="form-group">
				    <div class="input-group">
				       <div class="input-group-addon "><span style="color:red;"> </span>附件上传:</div>
				       <div class="input-group-addon">
				       		<input type="hidden" name="attachmentIds" id="attachmentIds">
				       		
					       				<span class="glyphicon glyphicon-plus">
					       					<input type="file" class="attachmentFile" name="attachmentFile" id="attachmentFile"
						       				 onchange="uploadAttachment('${basePath }','${id}');"/>
					       				</span>
				       </div>
				    </div>
				  </div>
				  <!-- 附件 -->
				  <ul class="body-file-ul">
				  		<c:forEach items="${attachmentFileList }" var="v" varStatus="sta">
		    	            <li>
					  			<a href="${simplePath }${v.revealpath }">
					  				<c:choose>
			              		 		<c:when test="${fn:length(v.filename) < 20 }">
			              		 			${v.filename }
			              		 		</c:when>
			              		 		<c:otherwise>
			              		 			${fn:substring(v.filename,0,20) }....${v.extname }
			              		 		</c:otherwise>
			              		 	</c:choose>
					  			</a>
					
					       				<i class="del_icon" onclick="deleteImage('${v.id }',this,'${basePath }')"></i>
		    	            </li>
				  		</c:forEach>
				  </ul>
				<!-- 附件上传 -->  
				    

     三.初始化jssdk:  

          controller

@Controller
@RequestMapping("jssdk")
public class WeXinJsSdkController {
	
	@Autowired
	private WxMpService wxMpService;
	
	@RequestMapping(value = "/config", method = RequestMethod.GET)
	@ResponseBody
	public WxJsapiSignature wxJsSdkConfig(HttpServletRequest request,String url) {
		try {
			WxJsapiSignature wxJsapiSignature = wxMpService.createJsapiSignature(url);
			return wxJsapiSignature;
		} catch (WxErrorException e) {
			return null;
		}
	}
	  
}
  js请求获取config 参数 
	/* 初始化jssdk */
	$.get("${basePath}/jssdk/config.do",{url:window.location.href},function(data,status){
		if(status == "success"){
			wx.config({
			    debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
			    appId: data.appId, // 必填,公众号的唯一标识
			    timestamp: data.timestamp, // 必填,生成签名的时间戳
			    nonceStr: data.nonceStr, // 必填,生成签名的随机串
			    signature: data.signature,// 必填,签名,见附录1
			    jsApiList: ['chooseImage', 'uploadImage', 'downloadImage', 'previewImage'] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2
			 
			});
			wx.ready(function(){
				//layer.msg("jssdk初始化成功");
			    // config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后,config是一个客户端的异步操作,
			    //所以如果需要在页面加载时就调用相关接口,则须把相关接口放在ready函数中调用来确保正确执行。对于用户触发时才调用的接口,则可以直接调用,不需要放在ready函数中。
			});
			wx.error(function(res){
			    // config信息验证失败会执行error函数,如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,
			    //也可以在返回的res参数中查看,对于SPA可以在这里更新签名。
				layer.msg(res);
			});
		}
		},"json");

  js 配置

    1. chooseImage    手机选择或拍摄图片 
    2. uploadImage    上传图片到微信服务器
    3. downloadImage  下载图片到本地

    previewImage   本地图片预览 


	
	//微信sdk上传图片------------------------
var images = {
	    localIds: [],
	    serverIds: [],
	  }; 
function chooseImage(basePath,id){
	wx.chooseImage({
	    count: 6, // 默认9
	    sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有
	    sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有
	    success: function (res) {
	    	images.localIds = res.localIds; // 返回选定照片的本地ID列表,localId可以作为img标签的src属性显示图片
	    	//显示缩略图
	        for(var i=0;i<images.localIds.length;i++){
	        	var liLength = $(".body-img-ul li").length;
	        	if(liLength >= 6 ){
	        		layer.msg("最多允许上传6张图片!");
					return;
		        }
	        	var imgHtml = "<li style=\"background-image:url('"+images.localIds[i]+"')\"";
	        		   imgHtml += "onclick=\"previewImage('"+images.localIds[i]+"')\">";
	        		   imgHtml += "<i class=\"del_icon\"></i>";
	        		   imgHtml += "</li>";
	  				
	        	$(".body-img-ul").append(imgHtml);
		    }
		    //递归的上传图片
	        syncUpload(images.localIds,id);
	    }
	});
}
//将微信服务器的图片下载到本地
var syncUpload = function(localIds,id){
	var localId = localIds.pop();
	//上传图片到微信服务器
    wx.uploadImage({
        localId: localId, // 需要上传的图片的本地ID,由chooseImage接口获得
        isShowProgressTips: 1, // 默认为1,显示进度提示
        success: function (res) {
        	images.serverIds.push(res.serverId); // 返回图片的服务器端ID
            if(localIds.length > 0){
            	syncUpload(localIds,id);  
            }else if(images.localIds.length == 0){
            	var serverids = images.serverIds.join(',');
            	$.post(
            		basePath+"/fileupload/uploadImg.do",
            		{"serverIds":serverids,"id":id}
            		,function(data,status){
        			if(status == "success"){
        				images.serverIds = [];
        				//存储文件id到页面
        				setFileIds(data.join(','));
        				
        				var tempLen = $(".del_icon").length;
        				var dataLen = data.length;
        				var del_index;
        				var del_id;
        				for(var i = 1;i<dataLen+1;i++){
        					del_index = tempLen-i;
        					del_id = data[i-1];
        					addDeleteEvent(del_index,del_id,basePath);
        				}
        				layer.msg("上传成功!");
        			}
        	  },"json");
            }
        }
    });
 }
//添加事件
function addDeleteEvent(del_index,del_id,basePath){
	$(".del_icon").eq(del_index).click(function(e){
		deleteImage(del_id,this,basePath);
		window.event.cancelBubble = true;
	});
}
//添加上传的filemodeID
function setFileIds(fildIds){
	var temp = $("#fileIds").val();
	if(temp == ""){
		 $("#fileIds").val(fildIds)
	}else{
		$("#fileIds").val(temp+","+fildIds)
	}
}
//预览图片
function previewImage(url){
	document.write("url----"+url);
	 wx.previewImage({
         current: url, // 当前显示图片的http链接
         urls: [url] // 需要预览的图片http链接列表
	 });
}
//异步删除图片
function deleteImage(id,obj,basePath){
	$.ajax({
		  type: 'POST',
		  url: basePath+"/fileupload/deleteFile.do",
		  data:{"id":id},
		  dataType: 'json',
		  success: function(data){
			 if(data.state == 200){
				layer.msg("删除成功!");
				 //移除对应数据
				 $(obj).parent()[0].remove();
			 }else{
				 layer.msg("删除失败!");
			 }
		  }
		});
	 window.event.cancelBubble = true;
}
//微信sdk上传图片------------------------
//上传附件
function uploadAttachment(basePath,id) {
   //限制数量
	var liLength = $(".body-file-ul li").length;
	if(liLength >= 6 ){
		layer.msg("最多允许上传6份附件!");
		return;
    }
   //上传文件
   $.ajaxFileUpload({
       url: basePath+"/fileupload/uploadAttachment.do", //文件上传到哪个地址,告诉ajaxFileUpload
       data:{"bussinessId":bussinessId,"id":id},
       secureuri: false, //一般设置为false
       fileElementId: 'attachmentFile', //文件上传控件的Id  <input type="file" id="fileUpload" name="file" />
       dataType: 'json', //返回值类型 一般设置为json
       success: function (data, status){//服务器成功响应处理函数
    	   if(data.state == 200){
    		   var imgHtml = "<li>";
    		   imgHtml += "<a href=\""+basePath+data.data[2]+"\">"+data.data[1]+"</a>";
    		   imgHtml += "<i class=\"del_icon\" onclick=\"deleteImage('"+data.data[0]+"',this,'"+basePath+"')\"></i>";
    		   imgHtml += "</li>";
    		   $(".body-file-ul").append(imgHtml)
    		   //存储文件id到页面
    		   setFileIds(data.data[0]);
    	   }
		   layer.msg(data.msg);
       	}
       });
   };
	
.后台配置文件名称,设置本地存储路径,把相应数据存储到本地:  

@Controller
@RequestMapping("/fileupload")
public class FileUploadController {
	
	public String prefix ="yongjun/upload/";
	private Logger logger = LoggerFactory.getLogger(this.getClass());
	@Autowired
	private WxMpService wxMpService;
	@Autowired
	private UsersService usersService;
	@Autowired
	private FileModelService fileModelService;
	@Value(value = "#{resourceProperties['uploadUrl']}")
	private String uploadUrl;
	
	@RequestMapping(value = "/uploadImg", method = RequestMethod.POST)
	@ResponseBody
	public String[] uploadImg(String serverIds,BigDecimal id,HttpServletRequest request){
		
		String[] result;
		//获取当前用户
		CurrentUser currentUser = usersService.getCurrentUser(request);
		try {
			String[] serverId_arr = serverIds.split(",");
			if(serverId_arr != null && currentUser != null){
				//图片存储
				List<FileModel> fileModelStorelist = new ArrayList<FileModel>();
				for (int i=0;i<serverId_arr.length;i++) {
					File file = wxMpService.getMaterialService().mediaDownload(serverId_arr[i]);
					String filePath = FileUploadUtil.uploadImg(file, uploadUrl, currentUser);
					//文件模型数据
					FileModel fileModel = new FileModel();
					fileModel.setCreatedTime(new Date());
					byte disabled = 0;
					fileModel.setDisabled(disabled);
					fileModel.setFilename(file.getName());
					fileModel.setRealname(file.getName());
					fileModel.setExtname(filePath.substring(filePath.lastIndexOf(".")+1));
					fileModel.setFilepath(filePath);
					fileModel.setRevealpath(filePath.replace("C:\\crm2009\\upload", "/myImg").replace("\\", "/"));
					fileModel.setFiletype("image");
					
					fileModelStorelist.add(fileModel);
				}
				result = fileModelService.storeFileModelList(fileModelStorelist);
				return result;
			}else{
				logger.info("当前用户不存在!!");
				return null;
			}
		} catch (Exception e) {
			e.printStackTrace();
			logger.info("下载文件失败!!"+e.getClass());
			return null;
		}
		
		
	}
	@RequestMapping(value = "/uploadAttachment", method = RequestMethod.POST)
	@ResponseBody
	public ResultData uploadAttachment(BigDecimal bussinessId,BigDecimal id,HttpServletRequest request,MultipartFile attachmentFile){
		
		//判断文件类型
		String originalFilename = attachmentFile.getOriginalFilename();
		String[] imagType={".jpg",".png",".bmp",".gif"};
		List<String> imagTypeList = Arrays.asList(imagType);
		if(imagTypeList.contains(originalFilename.substring(originalFilename.lastIndexOf(".")).toLowerCase())){
			return ResultData.error("无法上传图片!");
		}
		String[] result = new String[3];
		//获取当前用户
		CurrentUser currentUser = usersService.getCurrentUser(request);
		try {
			if(currentUser != null){
				//上传文件
				String filePath = FileUploadUtil.uploadFile(attachmentFile, uploadUrl, currentUser);
				//文件模型数据
				FileModel fileModel = new FileModel();
				fileModel.setCreatedTime(new Date());
				byte disabled = 0;
				fileModel.setBussinessid(bussinessId);
				fileModel.setDisabled(disabled);
				fileModel.setFilename(originalFilename);
				fileModel.setRealname(filePath.substring(filePath.lastIndexOf("/")+1));
				fileModel.setExtname(filePath.substring(filePath.lastIndexOf(".")+1));
				fileModel.setFilepath(filePath);
				fileModel.setRevealpath(filePath.replace("C:\\crm2009\\upload", "/myImg").replace("\\", "/"));
				fileModel.setFiletype("attachment");
				fileModelService.storeFileModel(fileModel);
				
				result[0] = fileModel.getId().toString();
				result[1] = fileModel.getFilename();
				result[2] = fileModel.getRevealpath();
				return ResultData.ok(result, "上传成功!");
			}else{
				logger.info("当前用户不存在!!");
				return null;
			}
		} catch (Exception e) {
			e.printStackTrace();
			logger.info("下载文件失败!!"+e.getClass());
			return null;
		}
		
		
	}
	
	@RequestMapping(value = "/deleteFile", method = RequestMethod.POST)
	@ResponseBody
	public ResultData deleteFile(BigDecimal id){
		fileModelService.deleteFileModel(id);
		return ResultData.ok();
	}
	
}
.设置 文件转接工具类:

     

public class FileUploadUtil {
	/**
	 * 上传文件
	 * @param file 源文件
	 * @param uploadUrl 上传地址
	 * @param currentUser 当前用户
	 * @param fileType 
	 * @return
	 */
	public  static String uploadImg(File file,String uploadUrl,CurrentUser currentUser) {
		//处理待写入的位置
		if(currentUser != null){
			uploadUrl += "\\image\\" +currentUser.getLoginName().toString();
		}
		File uploadPosition = new File(uploadUrl);
		if(!uploadPosition.exists()){
			//文件路径不存在,则创建
			uploadPosition.mkdirs();
		}
		if (file.renameTo(new File(uploadPosition + "\\" + file.getName()))) {
            System.out.println("File is moved successful!");
        } else {
            System.out.println("File is failed to move!");
        }
		return uploadPosition + "/" + file.getName();
	}
	public  static String uploadFile(MultipartFile file,String uploadUrl,CurrentUser currentUser) throws UnsupportedEncodingException {
		//处理待写入的位置
		if(currentUser != null){
			uploadUrl += "\\attachment\\" +currentUser.getLoginName().toString();
		}
		File uploadPosition = new File(uploadUrl);
		if(!uploadPosition.exists()){
			//文件路径不存在,则创建
			uploadPosition.mkdirs();
		}
		String originalFilename = file.getOriginalFilename();
		String realName = UUID.randomUUID().toString() + originalFilename.substring(originalFilename.lastIndexOf("."));
		try {
			file.transferTo(new File(uploadPosition + "\\" +  realName));
			System.out.println("File is moved successful!");
		} catch (IllegalStateException | IOException e) {
			e.printStackTrace();
			System.out.println("File is failed to move!");
		}
		return uploadPosition + "/" + realName;
	}
} 
   学习在于不断地探索、思考和总结记录,欢迎喜欢的朋友们在下方留言,与君共同进步!







2014-07-25 12:47:03 buerkai 阅读数 173
  • 微信支付开发-微信公众号开发12-微信开发php

    微信公众平台开发之微信支付开发是子恒老师《微信公众平台开发》视频教程的第12部。详细讲解了用php进行微信支付的开发。内容包含获取支付密钥,微信公众号支付开发,扫码支付,微信刷卡支付,异步处理支付结果等等。欢迎反馈,微信/QQ:68183131

    27864 人正在学习 去看看 秦子恒

ComponentName comp = new ComponentName("com.tencent.mm", "com.tencent.mm.ui.tools.ShareToTimeLineUI");
    intent.setComponent(comp);
    intent.setAction(Intent.ACTION_SEND);
    intent.setType("image/*");
    intent.putExtra(Intent.EXTRA_TEXT, msg);
    intent.putExtra("Kdescription", msg);
    ContentResolver cr = ZJApplication.getInstance().getContentResolver();
    String url = MediaStore.Images.Media.insertImage(cr, PictureHelper.getBitMapFromResource(R.drawable.ic_launcher), "share.png", "");
    intent.putExtra(Intent.EXTRA_STREAM, Uri.parse(url));

2018-04-08 19:59:57 lc_miao 阅读数 20653
  • 微信支付开发-微信公众号开发12-微信开发php

    微信公众平台开发之微信支付开发是子恒老师《微信公众平台开发》视频教程的第12部。详细讲解了用php进行微信支付的开发。内容包含获取支付密钥,微信公众号支付开发,扫码支付,微信刷卡支付,异步处理支付结果等等。欢迎反馈,微信/QQ:68183131

    27864 人正在学习 去看看 秦子恒

一、申请应用


1、首先到 [ 微信开放平台官网] 申请注册帐号,这些流程就忽略了到官网一看自然就知道怎么走,感觉在这里没有必要说很多。
2、申请一个移动应用,填写完成你的应用信息。其他的没啥,最主要的是要填对你的签名和包名,否则SDK调不起来。包名大家都懂,主要是这个签名的过程需要提醒一下:
1)签名全部是小写,没有大写。
2)签名填的是MD5,不是SHA1更不是SHA256,有的同学不小心就搞错了
这里获取应用签名的方法有二。

第一个方法:先假设我们的应用已经有签名文件了(这里就不讲解生成签名文件的过程了,Android Studio提供的生成签名文件的方法已经很简单了,这里不赘述)。在命令行输入以下命令:

keytool -list -v -keystore 目标签名文件路径

然后输入一下签名文件的密码后就出来很多签名文件的信息:
签名
注意是复制出里面的指纹证书下的MD5那一串。
然后自己手动把大写字母改成小写字母,并且把冒号都去掉。这样子就得到了签名。

第二个方法:简单粗暴,微信开放平台提供给我们一个签名获取的工具,点击这里跳转
这里写图片描述
然后把apk安装到手机后,正确输入你的应用包名后就获取到了一串签名:
这里写图片描述

把这串签名填写到你申请的应用配置里面。

搞定,等待微信审核。
审核完成后得到AppID 和 AppSecret 。注意这两个,等下要用到。

二、配置微信分享的SDK


可以直接到官网下载jar包或者配置依赖:

dependencies {
    compile 'com.tencent.mm.opensdk:wechat-sdk-android-with-mta:+'
}

或者

dependencies {
    compile 'com.tencent.mm.opensdk:wechat-sdk-android-without-mta:+'
}

(其中,前者包含统计功能)

然后我们添加如下权限:


<uses-permission android:name="android.permission.INTERNET"/>

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>

<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>

<uses-permission android:name="android.permission.READ_PHONE_STATE"/>

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

三、微信分享示例


1、首先我们需要像微信注册我们申请的应用,在前面我们申请的应用审核完后的APP_ID 和APP_SECRET 就填写在这里:

private static final String APP_ID = "123456"; 
private static final String APP_SECRET = "123456";
private IWXAPI api;  
//向微信注册app
public void register(Context context) {   
	if(api==null){
	    api = WXAPIFactory.createWXAPI(context, APP_ID, true);  
	    api.registerApp(APP_ID);        
    }
}

这部分代码没有限制性需要在哪里执行,你可以放在Application的onCreate,也可以在Activity,笔者推荐放在你要做分享的Activity里。

接下来就是编写分享的代码了。微信分享有文字、图片、链接、音乐、视频这几种方式,分享的途径有分享到好友、分享到朋友圈。
由于不同分享方式的原理相同,这里我们就拿分享网页链接的例子来说
以下是分享网页链接的代码:

WXWebpageObject webpage = new WXWebpageObject();
        webpage.webpageUrl = "网页链接";
        final WXMediaMessage msg = new WXMediaMessage(webpage);
        msg.title = "网页标题";
        msg.description = "网页内容";
        
        Bitmap thumb = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.xxx);
        if(thumb != null) {
            Bitmap mBp = Bitmap.createScaledBitmap(thumb, 120, 120, true);
            thumb.recycle();
            msg.thumbData = bmpToByteArray(thumb,true);
        }
       SendMessageToWX.Req req = new SendMessageToWX.Req();    //创建一个请求对象
    req.message = msg; 
    //req.scene = SendMessageToWX.Req.WXSceneTimeline;    //设置发送到朋友圈
    req.scene = SendMessageToWX.Req.WXSceneSession;   //设置发送给朋友
    req.transaction = "设置一个tag";  //用于在回调中区分是哪个分享请求
    boolean successed = api.sendReq(req);   //如果调用成功微信,会返回true

好了,正常的话到这一步我们已经能够调起微信对好友分享网页链接了。
而往往事与愿违,现实和理想差距太大。有很多同学到这一部却碰到很多问题,比如:

1、调不起来,微信一点反应都没有;
2、能分享给好友、但是显示不了网页链接的图片;
3、能分享给好友,自己能看到网页链接的图片,但是朋友看不到。

问题的答案如下:
第一个问题:调不起来,微信一点反应都没有
首先你得保证填写的包名和签名是一定正确的,签名你如果不确定是否正确的话就最好用官方的签名获取apk来校验一下。
其次,你得确保有没有注册了?我们在代码的第一步写的:
WXAPIFactory.createWXAPI(context, APP_ID, true);
如果都确保是没问题的,那么就要从图标入手了,这是微信文档的一个大坑

我们在分享链接的时候填写图片,这个bitmap不能超过32kb
事实证明如果传给微信的bitmap太大的话,则微信会无任何反应,Log也看不出什么出错。
所以有了这一步:

Bitmap mBp = Bitmap.createScaledBitmap(thumb, 120, 120, true);

或者把你的图切小一点。也许就能调起来了。
第二个问题:能分享给好友、但是显示不了网页链接的图片
这个问题其实也是跟bitmap的大小有关,跟问题一差不多。所以建议两个方法,要么把图片切小一点。要么代码控制图片的大小,使之在正常的范围内。

第三个问题:能分享给好友,自己能看到网页链接的图片,但是朋友看不到
前面两个问题其实还好,至少稍微百度一下就能解决。而第三个问题着实坑爹。好友看不到你的网页图片,会让你误以为是问题二导致的,结果一顿修改都解决不了问题。
而其实,这个是跟微信的一个敏感词检测系统,如果你的网页分享内容里面包含了“红包”、“领取”等等之类的词语,极有可能造成问题三的产生,而微信目前没有公布哪些敏感词不通过,所以出现这个问题的话你一定要注意修改你的标题和内容了,只能把表达方式换一个,再试一下。

四、微信分享的结果回调


说了这么多,想必你已经能调起微信进行分享了吧。我们还没说到微信的回调,其实微信的回调说起来也是比较坑的,如果没接触过的话,这个回调也能耗了你不少时间。
微信的回调有四大坑,这里通过踩坑的方式来完成示例:

1、新建WXEntryActivity(坑一:新建的WXEntryActivity类包名路径不对)
长话短说,这里千言万语融成一句话:一定要在建立一个 包名.wxapi.WXEntryActivity
比如你的包名是:com.example.demo。那么你就得配置一个com.example.demo.wxapi.WXEntryActivity
千万要注意,不要随便乱放这个Activity,也不能改名,就叫WXEntryActivity。包名跟你申请应用的时候填写的包名一致。

2、向AndroidManifest.xml注册WXEntryActivity(坑二:没有配置android:exported=“true”)

  <activity android:name=".wxapi.WXEntryActivity"
            android:exported="true"
            android:launchMode="singleTop"
            ></activity>

注意看清楚没?多配置了一个 android:exported=“true”,这个属性代表着运行被外界程序所启动这个Activity,微信对于我们的app来说就是外部程序了,要回调我们的Activity,没有这个万万不行。千万要注意

3、实现IWXAPIEventHandler接口,注册API(坑三:没有在onCreate里注册API)
这个接口有两个方法:

@Override
    public void onReq(BaseReq baseReq) {
        
    }

    @Override
    public void onResp(BaseResp baseResp) {

    }

分别是我们做出的请求和请求的结果回调。我们的业务逻辑便是从onResp方法里写的
然后一定要 千万要 万万要注册api、八成没有接收到回调的同学们就是没有这一步:

  @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //注册API 没有这个不会执行onResp
        api = WXAPIFactory.createWXAPI(this, WXModel.APP_ID);
        api.handleIntent(getIntent(), this);
        LogUtils.i(TAG, " handleIntent:" + api.handleIntent(getIntent(), this));
    }

4、在onResp方法里编写分享后的回调处理逻辑(坑四:回调处理逻辑搞错)
其实到这一步回调就可以完成了。剩下的是我们的回调回来页面后的业务逻辑,从onResp来处理。
在这里特别注意提醒同学们,由于微信的登录和分享都是一样放在这个WXEntryActivity里面,所以在onResp方法里面我们必须做出区分,通过baseReq.getType()可以得到回调的类型,在ConstantsAPI接口里面定义了微信回调的很多类型,需要我们自己来区分
这里写图片描述

然后通过状态码:resp.errCode 来得知分享的结果(成功、失败、取消等等),正常成功的话是resp.errCode==BaseResp.ErrCode.ERR_OK,取消分享是BaseResp.ErrCode.ERR_USER_CANCEL。

好了,到这一步的时候我们就绕过这么多个坑,基本做对的话就能成功分享链接出去并且回调分享结果。

最后,由于微信的回调机制,我们没办法做出其他处理,一定只能通过在固定的包名路径下新建这个类来完成回调,如果你实在不想微信显示这个页面的话,那么也可以通过广播、或者EventBus来吧结果回调出去,然后结束页面。这样子注册了广播接收或者EventBus的Actibity就能接收到回调的结果:

 @Override
    public void onResp(BaseResp baseResp) {
        if (baseResp.getType() == ConstantsAPI.COMMAND_SENDMESSAGE_TO_WX) {
            Intent intent = new Intent("SHARE_WX_ACTION");
            intent.putExtra("errCode", baseResp.errCode);
            sendBroadcast(intent);
            finish();
        }
    }

五、微信分享的踩坑总结


到这里,微信分享的过程就结束了,你学会了吗?
由于坑比较多,在最后我们再总结下微信分享都有哪些需要注意的地方:

1、配置签名的坑
解决:一定要MD5、小写、不要带冒号,最好用官方给的签名获取工具。
2、没有注册API
解决:API是运行重复注册的,并且也不是耗时任务,所以不妨放在Activity的onCreate下面,记得写这个。
3、分享的图标
解决:分享的图标大小不能超过32K,要是jpg格式。
3、分享图标自己能看到,好友看不到
解决:修改分享的标题和内容,不要触及微信的敏感词检测系统,多试几下。
4、没有回调
解决:一定要注意WXEntryActivity的包名路径是否正确,已经配置export=true
5、确定了问题4后还是调不起来
你确定有在WXEntryActivity的onCreate里面注册API并且调用 api.handleIntent(getIntent(), this);? 估计你没有吧?
6、回调到其他页面
解决:没办法直接回调到其他页面,但可以通过广播、EventBus等通知的实现来通知其他页面刷新,同时结束WXEntryActivity的页面。

2016-12-14 10:25:51 u013241923 阅读数 8501
  • 微信支付开发-微信公众号开发12-微信开发php

    微信公众平台开发之微信支付开发是子恒老师《微信公众平台开发》视频教程的第12部。详细讲解了用php进行微信支付的开发。内容包含获取支付密钥,微信公众号支付开发,扫码支付,微信刷卡支付,异步处理支付结果等等。欢迎反馈,微信/QQ:68183131

    27864 人正在学习 去看看 秦子恒

开始创建应用,通过审核等 我就不说了..

首先下载,微信的SDK

然后将jar包导入项目  ,可参考微信开发文档,然后有文档了,为什么还要写这个文章?对吧 

我只能吐槽,写开发文档的人太懒了.,好多没写明白

private String APP_ID = "00000000000000000"; //微信 APPID
private IWXAPI iwxapi;

private void regToWx() {
    iwxapi = WXAPIFactory.createWXAPI(context, APP_ID, true);//这里context记得初始化
    iwxapi.registerApp(APP_ID);
}
IMServer.getDiskBitmap(IMServer.url);
这个是我写的 一个从内存卡读取照片的类..   可根据自己需求更改

private void wxShare() {
    Bitmap bp = IMServer.getDiskBitmap(IMServer.url);
    WXImageObject wxImageObject = new WXImageObject(bp);
    WXMediaMessage msg = new WXMediaMessage();
    msg.mediaObject = wxImageObject;
    //设置缩略图
    Bitmap mBp = Bitmap.createScaledBitmap(bp, 120, 120, true);
    bp.recycle();
    msg.thumbData = bmpToByteArray(mBp, true);
    SendMessageToWX.Req req = new SendMessageToWX.Req();
    req.transaction = buildTransaction("img");//  transaction字段用
    req.message = msg;
    req.scene = SendMessageToWX.Req.WXSceneSession;
    iwxapi.sendReq(req);
}
我先上代码,我们看看上面的代码..设置缩略图那

官方给的 代码是  

msg.thumbData = Util.bmpToByteArray(thumbBitmap, true);


然后Util类,居然找不到(我用了qq的jar包,只能在这里找到.....)

所以我只能去demo里面找,然后把bmpToByteArray方法提取出来,如下

public static byte[] bmpToByteArray(final Bitmap bmp, final boolean needRecycle) {
    ByteArrayOutputStream output = new ByteArrayOutputStream();
    bmp.compress(Bitmap.CompressFormat.PNG, 100, output);
    if (needRecycle) {
        bmp.recycle();
    }

    byte[] result = output.toByteArray();
    try {
        output.close();
    } catch (Exception e) {
        e.printStackTrace();
    }
    return result;
}

然后再往下看,

    req.transaction = buildTransaction("img");//  transaction字段用

很明显  后面的是一个方法, 官方也没给出...   老方法 ,去demo里面找,如下

private String buildTransaction(final String type) {
    return (type == null) ? String.valueOf(System.currentTimeMillis()) : type + System.currentTimeMillis();
}


没有更多推荐了,返回首页