微信公众号开发经验总结
微信公眾號(hào)開發(fā)經(jīng)驗(yàn)總結(jié)
1.??????????????? 快捷訪問(wèn)
1.1?? 測(cè)試公眾號(hào)注冊(cè):
http://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login
1.2?? 微信公眾號(hào)開發(fā)指南:
https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1445241432
1.3?? 微信全局返回碼說(shuō)明:
https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1433747234
1.4?? 微信公眾號(hào)接口權(quán)限說(shuō)明:
???????? https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1433401084
?
2????? ?注冊(cè)一個(gè)公眾號(hào)賬戶(也可先用測(cè)試公眾號(hào)調(diào)試環(huán)境)
2.1????????????? 賬戶注冊(cè)
2.1.1????????? 正確填寫下列信息
?
注意:
URL必須以http:// 或者h(yuǎn)ttps:// 開頭,分別支持80端口和443端口;
Token必須為英文或者數(shù)字,長(zhǎng)度為3-32字符;
?
2.1.2????????? 開發(fā)過(guò)程中,需要注意接口的調(diào)用次數(shù),
?
2.1.3????????? 注意檢查URL和JS接口安全域名,可以保持一致,也可以不一樣,個(gè)人建議最好保持一致;
?
3????? 測(cè)試開發(fā)環(huán)境
Eg:
微信網(wǎng)頁(yè)授權(quán)access_token和普通access_token區(qū)別
https://blog.csdn.net/benben_2015/article/details/79703508
微信獲取用戶信息的兩個(gè)接口和兩個(gè)ACCESS_TOKEN
?????????? https://www.cnblogs.com/sxmny/articles/4969119.html
?
2.1 記錄全局定量(獲取Token的兩個(gè)定量,在微信公眾號(hào)-賬戶信息處可以查詢):
Appid?????????????????? 應(yīng)用ID
AppSecret????????? 應(yīng)用密匙
2.2 獲取普通access_token(access_token是公眾號(hào)的全局唯一接口調(diào)用憑據(jù),公眾號(hào)調(diào)用各接口時(shí)都需要使用access_token)
調(diào)用接口:
URL:? ????? https://api.weixin.qq.com/cgi-bin/token
參數(shù): ??? grant_type : “client_credential”--------獲取access_token填寫client_credential???????????? ? ???? ???????? Appid : Appid---------第三方用戶唯一憑證
??????????????????? AppSecret : AppSecret-----------第三方用戶唯一憑證密鑰,即appsecret
返回參數(shù):????? access_token---------獲取到的憑證
???????????????????????????? expires_in----------憑證有效事件,單位:秒
注:公眾號(hào)對(duì)此接口的調(diào)用是有次數(shù)限制的,默認(rèn)是2000,花錢可增加次數(shù),獲取的access_token的有效期默認(rèn)是2小時(shí),故而,在開發(fā)過(guò)程中,可將調(diào)用此接口的access_token設(shè)為全局變量,并將次接口的調(diào)用方法設(shè)為全局自動(dòng)調(diào)用,獨(dú)立出來(lái);
Eg(將獲取access_token的接口獨(dú)立,并調(diào)整為100分鐘自動(dòng)刷新):
package com.gzh.api.utils;
?
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.atomic.AtomicInteger;
?
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Maps;
import com.jfinal.kit.Prop;
import com.jfinal.kit.PropKit;
?
import cn.hutool.http.HttpUtil;
?
/**
?* access_token 自動(dòng)刷新
?* @author Administrator
?*
?*/
public class WxToken {
?
?? public static String globalTokenObject = "";//調(diào)取接口的返回值參數(shù)(access_token、expires_in)
?? public static String globalToken = "";// access_token
?? public static AtomicInteger atomicInteger = new AtomicInteger();//計(jì)數(shù)器,統(tǒng)計(jì)接口調(diào)用的次數(shù)
?? public static String appid = "**************";
?? public static String appsecret = "*************";
?? static {
????? new Timer().schedule(new TimerTask() {
??????? @Override
??????? public void run() {
?????????? Map<String,Object> paramMap = Maps.newHashMap();
?????????? paramMap.put("appid", appid);
?????????? paramMap.put("secret", appsecret);
?????????? paramMap.put("grant_type", "client_credential");
?????????? globalTokenObject = HttpUtil.get("https://api.weixin.qq.com/cgi-bin/token", paramMap);
??????????
?????????? JSONObject wxToken = JSONObject.parseObject(globalTokenObject);
?????????? globalToken = HttpUtil.get("https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=" + wxToken.getString("access_token") + "&type=jsapi");???? ?????
?????????? atomicInteger.incrementAndGet();
??????? }
????? }, 0, 2*50*60*1000);
?? }?
}
?
2.3 獲取用戶Code/微信訪問(wèn)公眾號(hào)地址
使用微信打開如下地址https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect
?
參數(shù):appid---------appid
redirect_uri-------系統(tǒng)路徑,需與微信公中號(hào)綁定路徑一致
response_type-------“code”字符串
scope------“snsapi_userinfo”字符串
state------“1”字符串
?
(注意這個(gè)接口中有個(gè)參數(shù)scope 默認(rèn)有2個(gè)值snsapi_base和snsapi_userinfo,這個(gè)接口會(huì)根據(jù)scope 來(lái)生成不同的code并且獲取不同作用的access_token?,不管scope傳什么值都能在得到對(duì)應(yīng)access_token的同時(shí)得到open_id, 如果你只需要得到opend_id 那使用snsapi_base參數(shù)到此結(jié)束了,如果需要獲取用戶的其他信息比如 昵稱 地址 就要snsapi_userinfo 會(huì)彈出授權(quán))
eg:
https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx97967746637ee7c4&redirect_uri=http://www.HuaLuo.com/gzh/gzh/app.html&response_type=code&scope=snsapi_userinfo&state=1#wechat_redirect
直接在微信中訪問(wèn),系統(tǒng)中用如下方法接收:
//獲取用戶code?????? --var code = getCode(‘code’);
Function getCode(name){
??????? var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)", "i");
var r = window.location.search.substr(1).match(reg);
if (r != null) return unescape(r[2]); return null;
}
即可拿到用戶Code;
2.4 獲取openid
簡(jiǎn)單的實(shí)現(xiàn)微信獲取openid
https://blog.csdn.net/z880698223/article/details/78485243/
????? 1.調(diào)用https://open.weixin.qq.com/connect/oauth2/authorize接口獲取到code
《參數(shù)2.3》
2.得到code作為一個(gè)參數(shù)調(diào)用https://api.weixin.qq.com/sns/oauth2/access_token接口獲取到openid 《參照2.5》
2.5 獲取微信網(wǎng)頁(yè)授權(quán)access_token
調(diào)用接口:
URL: ???????? https://api.weixin.qq.com/sns/oauth2/access_token
參數(shù):?????
grant_type : “authorization_code”--------填寫client_credential
Appid : Appid---------第三方用戶唯一憑證
AppSecret : AppSecret-----------第三方用戶唯一憑證密鑰,即appsecret
Code:Code----------標(biāo)識(shí)不同的用戶code 《參照2.3》
返回參數(shù):
Openid
access_token
?
2.6 獲取微信用戶信息
???????? EG:
https://api.weixin.qq.com/cgi-bin/user/info?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN
???????? 調(diào)用接口:
???????? URL: ?????? https://api.weixin.qq.com/sns/userinfo
???????? 參數(shù):
???????? ???????? Openid-----《參照2.5》
?????????????????? Lang----“zh_CN”字符串
?????????????????? access_token------《參照2》>(注:此access_token網(wǎng)上資料說(shuō)需要普通access_token,及調(diào)用《2.2》接口返回的普通access_token,但是實(shí)際中發(fā)現(xiàn)調(diào)用《2.5》接口返回的網(wǎng)頁(yè)授權(quán)access_token也可以拿到用戶信息,故而實(shí)際開發(fā)中可換著調(diào)用調(diào)試,個(gè)人建議使用普通access_token,因?yàn)殚_文提出將此參數(shù)定義為全局變量,故而對(duì)代碼一致性維護(hù)較方便)
???????? 返回參數(shù):
???????? Subscribe--------用戶是否訂閱該公眾號(hào)標(biāo)識(shí),值為0時(shí),代表此用戶沒有關(guān)注該公眾號(hào),拉取不到其余信息
???????? Openid----------用戶的標(biāo)識(shí),對(duì)當(dāng)前公眾號(hào)唯一
???????? Nickname-----------用戶的昵稱
???????? Sex----------用戶的性別,值為1時(shí)是男性,值為2時(shí)是女性,值為0時(shí)是未知
???????? City----------用戶所在城市
???????? Country-------------用戶所在國(guó)家
???????? Province-----------用戶所在省份
???????? Language---------用戶的語(yǔ)言,簡(jiǎn)體中文為zh_CN
???????? Headimgurl-----------用戶頭像,最后一個(gè)數(shù)值代表正方形頭像大小(有0、46、64、96、132數(shù)值可選,0代表640*640正方形頭像),用戶沒有頭像時(shí)該項(xiàng)為空。若用戶更換頭像,原有頭像URL將失效
???????? subscribe_time-------------用戶關(guān)注時(shí)間,為時(shí)間戳。如果用戶曾多次關(guān)注,則取最后關(guān)注時(shí)間
???????? unionid------------只有在用戶將公眾號(hào)綁定到微信開放平臺(tái)帳號(hào)后,才會(huì)出現(xiàn)該字段
???????? remark-----------公眾號(hào)運(yùn)營(yíng)者對(duì)粉絲的備注,公眾號(hào)運(yùn)營(yíng)者可在微信公眾平臺(tái)用戶管理界面對(duì)粉絲添加備注
???????? groupid ------------- 用戶所在的分組ID(兼容舊的用戶分組接口)
tagid_list ------------用戶被打上的標(biāo)簽ID列表
subscribe_scene----------- 返回用戶關(guān)注的渠道來(lái)源,ADD_SCENE_SEARCH 公眾號(hào)搜索,ADD_SCENE_ACCOUNT_MIGRATION 公眾號(hào)遷移,ADD_SCENE_PROFILE_CARD 名片分享,ADD_SCENE_QR_CODE 掃描二維碼,ADD_SCENEPROFILE LINK 圖文頁(yè)內(nèi)名稱點(diǎn)擊,ADD_SCENE_PROFILE_ITEM 圖文頁(yè)右上角菜單,ADD_SCENE_PAID 支付后關(guān)注,ADD_SCENE_OTHERS 其他
qr_scene ------------二維碼掃碼場(chǎng)景(開發(fā)者自定義)
qr_scene_str------------ 二維碼掃碼場(chǎng)景描述(開發(fā)者自定義)
?
2.7 調(diào)用微信JS-SDK,獲取jsapi_ticket
微信公眾平臺(tái)jsapi開發(fā)教程(1)獲取:
https://blog.csdn.net/linfanhehe/article/details/52354848
生成簽名之前必須先了解一下jsapi_ticket,jsapi_ticket是公眾號(hào)用于調(diào)用微信JS接口的臨時(shí)票據(jù)。正常情況下,jsapi_ticket的有效期為7200秒,通過(guò)access_token來(lái)獲取。由于獲取jsapi_ticket的api調(diào)用次數(shù)非常有限,頻繁刷新jsapi_ticket會(huì)導(dǎo)致api調(diào)用受限,影響自身業(yè)務(wù),開發(fā)者必須在自己的服務(wù)全局緩存jsapi_ticket 。《參照2.2》和我們獲取普通access_token類似,由于有訪問(wèn)次數(shù)的限制,我們將次參數(shù)調(diào)為全局自動(dòng)觸發(fā);
?
接口:
URL: https://api.weixin.qq.com/cgi-bin/ticket/getticket
參數(shù): access_token-----《參照2.2》
???????? Type----“jsapi”字符串
返回參數(shù):ticket------------調(diào)用微信jsapi的憑證票
????????
2.8 初始化微信config配置
所有需要使用JS-SDK的頁(yè)面必須先注入配置信息,否則將無(wú)法調(diào)用(同一個(gè)url僅需調(diào)用一次,對(duì)于變化url的SPA的web app可在每次url變化時(shí)進(jìn)行調(diào)用,目前Android微信客戶端不支持pushState的H5新特性,所以使用pushState來(lái)實(shí)現(xiàn)web app的頁(yè)面會(huì)導(dǎo)致簽名失敗,此問(wèn)題會(huì)在Android6.2中修復(fù))。
???????? 微信調(diào)取音頻、視頻、分享等接口前,需要先初始化驗(yàn)證
JS:
app.ajax('/wxInitConfig.action',{'url':location.href},function(configDate){
????????????? wx.config({
????????????????? debug: true, // 開啟調(diào)試模式,調(diào)用的所有api的返回值會(huì)在客戶端alert出來(lái),若要查看傳入的參數(shù),可以在pc端打開,參數(shù)信息會(huì)通過(guò)log打出,僅在pc端時(shí)才會(huì)打印。
????????????????? appId: configDate.appid, // 必填,公眾號(hào)的唯一標(biāo)識(shí)
????????????????? timestamp: configDate.timestamp, // 必填,生成簽名的時(shí)間戳
????????????????? nonceStr: configDate.nonceStr, // 必填,生成簽名的隨機(jī)串
????????????????? signature: configDate.signature,// 必填,簽名,見附錄1
????????????????? jsApiList: ['onMenuShareQQ', 'onMenuShareWeibo', 'onMenuShareTimeline','onMenuShareAppMessage','startRecord','stopRecord','onVoiceRecordEnd','playVoice','stopVoice','onVoicePlayEnd','uploadVoice','onMenuShareQZone'] // 必填,需要使用的JS接口列表,所有JS接口列表見附錄2 https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141115
????????????? });
?????????? });
ACTION:
public void wxInitConfig(){
?????? JSONObject wxToken = JSONObject.parseObject(WxToken.globalToken);《參照2.2代碼,獲取全局ticket》
?????? String jsapi_ticket = wxToken.getString("ticket");
?????? //此處如果沒有正常獲取到jsapi_ticket,請(qǐng)查看返回碼,在微信公眾號(hào)中查找錯(cuò)誤信息
?????? //前臺(tái)傳過(guò)來(lái)的當(dāng)前URL訪問(wèn)路徑
?????? String url = Dto.getParam().getString("url");
?
?????? //算法生成時(shí)間戳、簽名等信息,見下列代碼,直接附源碼,不多說(shuō)
?????? Map<String, String> ret = Sign.sign(jsapi_ticket, url);
?????? for (@SuppressWarnings("rawtypes") Map.Entry entry : ret.entrySet()) {
?????????? System.err.println(entry.getKey().toString() + " : " + entry.getValue());
?????? ??? Dto.getResult().set(entry.getKey().toString(), entry.getValue());
?????? }
?????? Dto.getResult().set("appid", wxProp.get("appid"));
??????? this.renderJson(Dto.getResult().getData());
??? }
?
///算法生成時(shí)間戳、簽名等信息/
過(guò)程中可能調(diào)用MessageDigest 包,請(qǐng)自行Baidu下載
package com.gzh.api.utils;
?
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;?
?
public class Sign {
??? public static void main(String[] args) {
??????? String jsapi_ticket = "jsapi_ticket";
??????? // 注意 URL 一定要?jiǎng)討B(tài)獲取,不能 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 = "";
?
??????? //注意這里參數(shù)名必須全部小寫,且必須有序
??????? 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);
??? }
}
?
2.9 調(diào)用微信JS-SDK實(shí)例(以錄音為例);
參考微信開發(fā)說(shuō)明文檔
https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141115
?
/* 開啟錄音*/
wx.startRecord({
success:function() {
?? alert(“接口調(diào)用成功”);
},
Fail:function(){
?? alert(“接口調(diào)用失敗”);//可以檢查access_token權(quán)限或者彈出調(diào)試,具體問(wèn)題具體分析《參照2.8》,開啟調(diào)試模式
},
Cancel:function(){
?? alert(“用戶拒絕提供錄音權(quán)限”);
}
});
/* 停止錄音*/
wx.stopRecord({
success: function (res) {
var localId = res.localId;
}
});
/* 監(jiān)聽錄音自動(dòng)停止(由于微信有錄音時(shí)限限制,一次錄音最大時(shí)長(zhǎng)為1分鐘,若超過(guò)此時(shí)=時(shí)限未停止錄音,則執(zhí)行此接口方法)*/
wx.onVoiceRecordEnd({// 錄音時(shí)間超過(guò)一分鐘沒有停止的時(shí)候會(huì)執(zhí)行 complete 回調(diào)
complete: function (res) {
var localId = res.localId;
}
});
注:我在此處有一個(gè)錄音時(shí)長(zhǎng)大于一分鐘的問(wèn)題,目前沒有好的解決方案,樓主在此處的解決方案是:當(dāng)執(zhí)行上述大于一分鐘回調(diào)時(shí),重新開啟錄音接口,直至手動(dòng)關(guān)閉錄音;將幾個(gè)錄音的localId存起來(lái),一并發(fā)至后臺(tái)獲取錄音文件,合成一份音頻文件,合成代碼網(wǎng)上找,但這個(gè)方法有一個(gè)Bug,在重新開啟錄音,即轉(zhuǎn)換的這個(gè)時(shí)間段,有大概1~2秒的錄音失效期;
? /* 播放錄音 */ wx.playVoice({localId: '' // 需要播放的音頻的本地ID,由stopRecord接口獲得
}); ? /* 暫停播放錄音 */ wx.pauseVoice({
localId: '' // 需要暫停的音頻的本地ID,由stopRecord接口獲得
});
?
/* 停止播放錄音 */ wx.stopVoice({localId: '' // 需要停止的音頻的本地ID,由stopRecord接口獲得
}); /* 監(jiān)聽錄音播放完畢接口 */ wx.onVoicePlayEnd({
success: function (res) {
var localId = res.localId; // 返回音頻的本地ID
}
}); ? /* 上傳錄音接口 */ wx.uploadVoice({
localId: '', // 需要上傳的音頻的本地ID,由stopRecord接口獲得
isShowProgressTips: 1, // 默認(rèn)為1,顯示進(jìn)度提示
success: function (res) {
var serverId = res.serverId; // 返回音頻的服務(wù)器端ID
}
}); 備注:上傳語(yǔ)音有效期3天,可用微信多媒體接口下載語(yǔ)音到自己的服務(wù)器,此處獲得的 serverId 即 media_id,參考文檔 .目前多媒體文件下載接口的頻率限制為10000次/天,如需要調(diào)高頻率,請(qǐng)登錄微信公眾平臺(tái),在開發(fā) - 接口權(quán)限的列表中,申請(qǐng)?zhí)岣吲R時(shí)上限。 ? 根據(jù)serverId獲取微信服務(wù)器上的錄音文件; 調(diào)用接口: URL: http://file.api.weixin.qq.com/cgi-bin/media/get 參數(shù): access_token------參照《2.2》 media_id----------serverId 返回參數(shù)是文件流,需自行解析;附實(shí)例; Eg:
/* (non-Javadoc)
?? ?* @see com.gzh.api.service.ReciteContentService#saveRecite(com.sdp.core.dt.DtoParam)
?? ?*/
?? @Override
?? public DtoResult saveRecite(DtoParam param) throws Exception{
????? /** 參數(shù)接收*/
????? String contentName = Dto.getParam().getString("contentName");//詩(shī)詞名稱
????? String contentId = Dto.getParam().getString("contentId");//詩(shī)詞ID
????? String fansId = Dto.getParam().getString("fansId");//粉絲ID
????? String serverId = Dto.getParam().getString("serverId");
?????
????? Record isFans = api.findFirst("select * from gzh_personalcenter where loginid = ?", fansId);
????? if(isFans != null && isFans.get("department") != null && !isFans.get("department").equals("")){
??????? String[] serverIds = Str.isNotAnyEmpty(serverId)?serverId.split(","):null;/* 當(dāng)傳過(guò)來(lái)的serverId有效時(shí)執(zhí)行*/
??????? if(serverIds != null && serverIds.length > 0){
?????????? System.err.println("正在上傳錄音文件...");
?????????? long time1 = System.currentTimeMillis();
?????????? /* 刪除舊錄音文件及數(shù)據(jù)*/
?????????? List<Record> deleteList = api.find("select * from gzh_recitecontent where fansid = ? and contentid = ?", fansId, contentId);
?????????? for(Record re : deleteList){
????????????? File file = new File(fileRoot + re.getStr("path"));
????????????? if(file.exists()) file.delete();
????????????? api.deleteById("gzh_recitecontent", re.getStr("id"));
?????????? }
??????????
?????????? //String token = HttpUtil.get("https://api.weixin.qq.com/cgi-bin/token", paramMap);
?????????? JSONObject wxToken = JSONObject.parseObject(WxToken.globalTokenObject);
??????????
?????????? List<String> tempRecites = new ArrayList<>();/* amr文件集合*/
?????????? List<String> newRecites = new ArrayList<>();/* mp3文件集合*/
?????????? /* 進(jìn)行臨時(shí)目錄創(chuàng)建*/
?????????? String tempResPath = fileRoot + tempPath;
?????????? if(!new File(tempResPath).exists()) new File(tempResPath).mkdirs();
?????????? /* 進(jìn)行錄音文件目錄創(chuàng)建*/
?????????? String realResPath = fileRoot + fileStore + recitePath + "/" + contentId + "/" + fansId;//文件實(shí)際存儲(chǔ)目錄
?????????? if(!new File(realResPath).exists()) new File(realResPath).mkdirs();//先進(jìn)行目錄創(chuàng)建操作
?????????? int size = 0;/* 記錄所有文件輸入流大小*/
?????????? /* 定義輸出流寫入?yún)?shù)*/
?????????? FileOutputStream fileOutputStream = null;
?????????? InputStream inputStream = null;
?????????? String fileName = ID.get();/* 文件名稱*/
?????????? /* 將文件分批取出并保存為MP3文件*/
?????????? for(int i = 0; i < serverIds.length; i++){
????????????? try{
???????????????? /* 從微信服務(wù)器獲取錄音文件,格式為amr*/
???????????????? String url = "http://file.api.weixin.qq.com/cgi-bin/media/get?access_token="
????????????????????? + wxToken.getString("access_token") + "&media_id=" + serverIds[i];
???????????????? URL urlGet = new URL(url);
???????????????? HttpURLConnection http = (HttpURLConnection) urlGet.openConnection();
???????????????? http.setRequestMethod("GET"); // 必須是get方式請(qǐng)求
???????????????? http.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
???????????????? http.setDoOutput(true);
???????????????? http.setDoInput(true);
//?????????????? System.setProperty("sun.net.client.defaultConnectTimeout", "3000");// 連接超時(shí)30秒
//?????????????? System.setProperty("sun.net.client.defaultReadTimeout", "3000"); // 讀取超時(shí)30秒
???????????????? http.connect();
???????????????? // 獲取文件轉(zhuǎn)化為byte流
???????????????? inputStream = http.getInputStream();
???????????????? size += inputStream.available();
???????????????? String tempFileName = tempResPath + "/" + fileName + (i+1) + ".amr";
???????????????? String recitFileName = tempResPath + "/" + fileName + (i+1) + ".mp3";
???????????????? if(serverIds.length == 1){
??????????????????? recitFileName = realResPath + "/" + fileName + ".mp3";
???????????????? }
???????????????? tempRecites.add(tempFileName);
???????????????? newRecites.add(recitFileName);
???????????????? byte[] data = new byte[1024 * 1024];
???????????????? int len = 0;
???????????????? fileOutputStream = new FileOutputStream(tempFileName);
???????????????? while ((len = inputStream.read(data)) != -1) {
??????????????????? fileOutputStream.write(data, 0, len);
???????????????? }
???????????????? /* 關(guān)閉流資源*/
???????????????? if(inputStream != null) inputStream.close();
???????????????? if(fileOutputStream != null) fileOutputStream.close();
???????????????? /* amr格式轉(zhuǎn)換成mp3格式*/
???????????????? File source = new File(tempFileName);
???????????????? File target = new File(recitFileName);
???????????????? AudioAttributes audio = new AudioAttributes();
???????????????? Encoder encoder = new Encoder();
???????????????? audio.setCodec("libmp3lame");
???????????????? EncodingAttributes attrs = new EncodingAttributes();
???????????????? attrs.setFormat("mp3");
???????????????? attrs.setAudioAttributes(audio);
???????????????? encoder.encode(source, target, attrs);/* 執(zhí)行轉(zhuǎn)換*/
????????????? }catch(Exception e) {
???????????????? System.err.println(e.getMessage());
????????????? }
?????????? }
?????????? System.err.println("輸入流大小:" + size);
?????????? File file = new File(realResPath + "/" + fileName + ".mp3");
?????????? if(newRecites.size() > 1){
????????????? try {
???????????????? combine(file.getPath(), newRecites);
????????????? } catch (Exception e) {
???????????????? System.err.println(e.getMessage());
????????????? }
?????????? }
?? ??????????? System.err.println("音頻大小:" + file.length()/1024 + "KB");
?? ??????????? //信息保存數(shù)據(jù)庫(kù)表
?? ??? ???? Record record = new Record();
?? ??? ???? record.set("id", ID.get());
?? ??? ???? record.set("contentname", contentName);//詩(shī)詞名稱
?? ??? ???? record.set("contentid", contentId);//詩(shī)詞ID
?? ??? ???? record.set("fansid", fansId);//粉絲ID
?? ??? ???? record.set("path", fileStore + recitePath + "/" + contentId + "/" + fansId + "/" + fileName + ".mp3");//吟誦錄音路徑
?? ??? ???? record.set("uploaddate", new Timestamp(new java.util.Date().getTime()));//吟誦時(shí)間
?? ??? ???? api.save(getTableName(), record);
?????????? long time2 = System.currentTimeMillis();
?????????? System.out.println("上傳時(shí)間:" + (time2 - time1)/1000 + "s");
?????????? /* 刪除臨時(shí)文件*/
?????????? for(int i = 0; i < tempRecites.size(); i++){
????????????? File tempFile = new File(tempRecites.get(i));
????????????? if(tempFile.exists()){
???????????????? System.out.println("刪除AMR臨時(shí)文件結(jié)果[" + tempRecites.get(i) + ":" + tempFile.delete() + "]");
????????????? }
????????????? if(tempRecites.size() > 1){
???????????????? tempFile = new File(newRecites.get(i));
???????????????? if(tempFile.exists()){
??????????????????? System.out.println("刪除MP3臨時(shí)文件結(jié)果[" + newRecites.get(i) + ":" + tempFile.delete() + "]");
???????????????? }
????????????? }
?????????? }
?? ??????? }
????? }else{
??????? Dto.getResult().setSuccessStatus("請(qǐng)完善個(gè)人信息");
????? }
????? return Dto.getResult();
?? }
??
?? /*將多個(gè)MP3文件合并*/
?? private static boolean combine(String outFile, List<String> inFiles) throws Exception{
????? File[] files = new File[inFiles.size()];
????? for(int i = 0; i < files.length; i++){
??????? files[i] = new File(inFiles.get(i));
????? }
????? FileInputStream fis = null;
????? FileOutputStream fos = new FileOutputStream(outFile, true);//合并其實(shí)就是文件的續(xù)寫,寫成true
????? for (int i = 0; i < files.length; i++){
??????? fis = new FileInputStream(files[i]);
??????? int len = 0;
??????? for (byte[] buf = new byte[1024 * 1024]; (len = fis.read(buf)) != -1;){
?????????? fos.write(buf, 0, len);
??????? }
??????? fis.close();
????? }
????? fos.close();
????? return true;
????? } ? /* 下載錄音接口 */ wx.downloadVoice({serverId: '', // 需要下載的音頻的服務(wù)器端ID,由uploadVoice接口獲得
isShowProgressTips: 1, // 默認(rèn)為1,顯示進(jìn)度提示
success: function (res) {
var localId = res.localId; // 返回音頻的本地ID
}
}); /* 此下載接口只是返回微信服務(wù)器端的文件localId ,然后調(diào)用播放接口讀取localId ,但是微信服務(wù)器端的錄音文件只保留3天,請(qǐng)知曉!*/ ? ? ? ?
轉(zhuǎn)載于:https://www.cnblogs.com/hualuo-sign/p/9732719.html
總結(jié)
以上是生活随笔為你收集整理的微信公众号开发经验总结的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 基于FPGA的数字电压表设计
- 下一篇: 但行好事,莫问前程!