微信公眾號開發之生成帶參數的二維碼
做微信公眾號開發的人員都知道用戶海報作為公眾號吸引用戶是常見的渠道,那么我就說說這個海報的生成。
看了好多公眾號發現他們的海報都是大同小異,一個漂亮的背景+自己的頭像+專屬二維碼。
背景就不闡述了,用戶的頭像可以由公眾號開發文檔提供的方式獲取(根據用戶的openId),今天主要講用戶的專屬二維碼
所謂專屬即便是一對一的。
二維碼分為兩種,臨時二維碼和永久二維碼
/*生成永久二維碼*/
public
static
String getPerpetualQR(String
account
){
//獲取數據的地址(微信提供)
String
url
=
"https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=
access_token (變量)
";
//發送給微信服務器的數據
String
jsonStr
=
"{\"action_name\": \"QR_LIMIT_SCENE\", \"action_info\":{\"scene\": {\"scene_id\": "
+
account
+
"}}}"
;
//post請求得到返回數據(這里是封裝過的,就是普通的java post請求)
String
response
= RequestMethod.sendPost(
jsonStr
,
url
);
return
response
.toString();
}
這里解釋一下發送的數據:
action_name:可以識別你要獲取的二維碼是永久性還是臨時的(
QR_LIMIT_SCENE :永久;QR_SCENE:臨時),
scene_id:要傳入的參數
服務器的返回數據
{"ticket":"gQHp7zoAAAAAAAAAASxodHRwOi8vd2VpeGluLnFxLmNvbS9xL0tEc29Sd0xsU2VzdzVHWTA2UmZSAAIEBQ5hVwMEAAAAAA==",
"url":"http:\/\/weixin.qq.com\/q\/KDsoRwLlSesw5GY06RfR"}
ticket:與上面傳入的參數是一一對應的,所以上面傳入的參數最好是唯一的可以標識的數據,而且用戶在掃二維碼的時候,如果是掃帶有參數的二維碼,那么微信服務器會返回這個ticket,具體可以看上一節(用戶關注)
url:二維碼地址,后台可以根據這個地址將二維碼下載到本地,具體的方法我會在下面提供一個。
到這里大家應該會疑問上面的
access_token (變量),這個是和微信服務器打交道的識別數據,這個數據必須有效才能進行一切的獲取數據操作,去開發文檔上看可以了解到這個數據由微信服務器提供,2個小時過期,但是每兩個小時之間會有一個過渡期,新老
access_token 都可以使用,不過這個過渡期的時間確實個謎,本人也未研究過,只是很多網友說這個過渡期是3-5分鍾。這里有個很大的坑,就是token沒有過期,但是微信服務器返回token失效,具體的解決方案待我下一節詳說。
以上是永久二維碼的獲取步驟,但是永久二維碼有一個缺陷,就是數量有限(目前最多為10萬個),臨時二維碼會更多,但是是否是無限的,這個待定。。。
/*生成臨時二維碼*/
public
static
String getTemporaryQR(String
scene_id
){
//獲取數據的地址(微信提供)
String
url
=
"https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=
access_token (變量)
";
//發送給微信服務器的數據
String
jsonStr
=
"{\"expire_seconds\": 2592000,\"action_name\": \"QR_SCENE\", \"action_info\": {\"scene\": {\"scene_id\": "
+
scene_id
+
"}}}"
;
//將得到的字符串轉化成json對象
String
response
= RequestMethod.sendPost(
jsonStr
,
url
);
return
response.toString();
}
發送的數據相對永久二維碼多了一個
expire_seconds,這是設定二維碼的有效期,以秒為單位,最多不超過30天,並且
action_name需要改成
QR_SCENE。
服務器的返回數據
{"ticket":"gQFt7zoAAAAAAAAAASxodHRwOi8vd2VpeGluLnFxLmNvbS9xL0F6dFktTVhsSV90YXNVX1ZtUlhSAAIE5hxiVwMEPAAAAA==",
"expire_seconds":60,
"url":"http:\/\/weixin.qq.com\/q\/AztY-MXlI_tasU_VmRXR"}
返回的數據相對永久二維碼就是多了一個expire_seconds,這個是有效期,過期掃碼無效。
現在大家都知道了永久二維碼和臨時二維碼之間的區別了,至於使用哪一種,根據需求變化。
參數與ticket之間是一一對應的,這也就達到了生成用戶海報的專屬二維碼了,至於這個參數的選定,本人可以給一個建議,將用戶的openId+當前時間作為參數,返回的ticket和用戶就可以達到一一對應的想過了,當二維碼過期,數據庫更新ticket這個數據就可以了,或者用戶每次獲取海報的時候就重新更新一個二維碼,反正臨時的很多。其他用戶掃這個二維碼的時候微信會將二維碼的ticket返回給我們,再根據ticket和openId的對應關系就可以知道當前被掃用戶是誰了。
下面提供一些方法:
//post請求
public
static
String sendPost(String
param
, String
url
) {
PrintWriter
out
=
null
;
BufferedReader
in
=
null
;
String
result
=
""
;
try
{
URL
realUrl
=
new
URL(
url
);
// 打開和URL之間的連接
URLConnection
conn
=
realUrl
.openConnection();
// 設置通用的請求屬性
conn
.setRequestProperty(
"accept"
,
"*/*"
);
conn
.setRequestProperty(
"connection"
,
"Keep-Alive"
);
conn
.setRequestProperty(
"user-agent"
,
"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"
);
// 發送POST請求必須設置如下兩行
conn
.setDoOutput(
true
);
conn
.setDoInput(
true
);
// 獲取URLConnection對象對應的輸出流
// out = new PrintWriter(conn.getOutputStream());
out
=
new
PrintWriter(
new
OutputStreamWriter(
conn
.getOutputStream(),
"utf-8"
));
// 發送請求參數
out
.print(
param
);
// flush輸出流的緩沖
out
.flush();
// 定義BufferedReader輸入流來讀取URL的響應
in
=
new
BufferedReader(
new
InputStreamReader(
conn
.getInputStream(),
"UTF-8"
));
String
line
;
while
((
line
=
in
.readLine()) !=
null
) {
result
+=
line
;
}
}
catch
(Exception
e
) {
System.
out
.println(
"發送 POST 請求出現異常!"
+
e
);
e
.printStackTrace();
}
// 使用finally塊來關閉輸出流、輸入流
finally
{
try
{
if
(
out
!=
null
) {
out
.close();
}
if
(
in
!=
null
) {
in
.close();
}
}
catch
(IOException
ex
) {
ex
.printStackTrace();
}
}
return
result
;
}
//根據url下載文件,參數(文件網址,存文件的本地地址)
public
static
Boolean downloadFile(String
urlString
, String
filePath
){
// 構造URL
URL
url
;
try
{
url
=
new
URL(
urlString
);
// 打開連接
URLConnection
con
;
try
{
con
=
url
.openConnection();
// 輸入流
InputStream
is
=
con
.getInputStream();
// 1K的數據緩沖
byte
[]
bs
=
new
byte
[1024];
// 讀取到的數據長度
int
len
;
// 輸出的文件流
OutputStream
os
=
new
FileOutputStream(
filePath
);
// 開始讀取
while
((
len
=
is
.read(
bs
)) != -1) {
os
.write(
bs
, 0,
len
);
}
// 完畢,關閉所有鏈接
os
.close();
is
.close();
return
true
;
}
catch
(IOException
e
) {
//
TODO
Auto-generated catch block
e
.printStackTrace();
return
false
;
}
}
catch
(MalformedURLException
e
) {
//
TODO
Auto-generated catch block
e
.printStackTrace();
return
false
;
}
}
