獲取小程序帶參二維碼并保存到本地
業(yè)務(wù)場(chǎng)景
下載并保存帶參數(shù)的小程序二維碼,用戶直接掃描帶參二維碼就進(jìn)入小程序,自動(dòng)根據(jù)參數(shù)完成部分業(yè)務(wù)。這個(gè)時(shí)候就需要用到微信小程序提供的二維碼接口 wxacode.getUnlimited ,官方文檔地址
網(wǎng)上介紹的很多方法有些過(guò)時(shí)了,有些不科學(xué),調(diào)用起來(lái)不方便,所以自己也總結(jié)了一份出來(lái)。雖然很久之前直接用jfinal+jfinal-wx寫(xiě)的,幾句代碼就搞定了,但是最近需要遷移項(xiàng)目到SpringBoot2,所以就踩了個(gè)坑順便總結(jié)一下,確實(shí)找了大半天。
POSTMAN調(diào)試
如果一個(gè)借口postman都調(diào)用不通,那就更別說(shuō)用java代碼去寫(xiě)了,調(diào)用接口第一步就是熟悉接口請(qǐng)求方式 method、請(qǐng)求格式 Content-Type和接口參數(shù) Body/Param以及返回的內(nèi)容,POSTMAN調(diào)用參數(shù)過(guò)程
url https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token={{access_token}} json raw { "scene":"EGAG-SAL**-******"}
WxUtil封裝:下載帶參數(shù)的小程序二維碼
import cn.hutool.http.HttpUtil; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import lombok.extern.slf4j.Slf4j; import java.io.*; import java.net.HttpURLConnection; import java.net.URL; import java.util.HashMap; import java.util.Map; /** * 微信小程序輔助工具類 https://developers.weixin.qq.com/miniprogram/dev/api-backend/ * @Author zhengkai.blog.csdn.net * */ @Slf4j public class WxUtil { public static String GET_MINICODE_URL="https://api.weixin.qq.com/wxa/getwxacodeunlimit"; public static String APPID="你的APPID"; public static String APPSECRET="你的APPSECRET"; public static String getAccessTokenAsUrl(){ String tokenStr=HttpUtil.get("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid="+APPID+"&secret="+APPSECRET+""); log.info(tokenStr); JSONObject jsonObject = JSONObject.parseObject(tokenStr); return "?access_token="+jsonObject.getString("access_token"); } /** * 下載帶參數(shù)的小程序二維碼 * https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/qr-code/wxacode.getUnlimited.html * by zhengkai.blog.csdn.net * @param pathStr 保存圖片的的path * @param certNumber 帶過(guò)去小程序的參數(shù),一般為你的業(yè)務(wù)參數(shù),建議是id或者number * */ public static String downloadMiniCode(String pathStr , String certNumber){ Map<String,Object> paramMap = new HashMap<>(); paramMap.put("scene",certNumber); paramMap.put("is_hyaline",true); String imgFilePath = pathStr+"/certificate/"+certNumber+".png";// 新生成的圖片 try { URL url = new URL(GET_MINICODE_URL+getAccessTokenAsUrl()); HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection(); httpURLConnection.setRequestMethod("POST");// 提交模式 httpURLConnection.setConnectTimeout(10000);//連接超時(shí) 單位毫秒 httpURLConnection.setReadTimeout(10000);//讀取超時(shí) 單位毫秒 // 發(fā)送POST請(qǐng)求必須設(shè)置如下兩行 httpURLConnection.setDoOutput(true); httpURLConnection.setDoInput(true); // 獲取URLConnection對(duì)象對(duì)應(yīng)的輸出流 PrintWriter printWriter = new PrintWriter(httpURLConnection.getOutputStream()); printWriter.write(JSON.toJSONString(paramMap)); // flush輸出流的緩沖 printWriter.flush(); //開(kāi)始獲取數(shù)據(jù) BufferedInputStream bis = new BufferedInputStream(httpURLConnection.getInputStream()); OutputStream os = new FileOutputStream(new File(imgFilePath)); int len; //設(shè)置緩沖寫(xiě)入 byte[] arr = new byte[2048]; while ((len = bis.read(arr)) != -1) { os.write(arr, 0, len); os.flush(); } os.close(); } catch (Exception e) { e.printStackTrace(); } return imgFilePath; } }
Controller調(diào)用
傳入保存的 path地址 和 scene參數(shù)
WxUtil.downloadMiniCode(storageService.getPathString(),certCompany.getCertNumber());
下載驗(yàn)證
之前試過(guò)很多方法就是保存后的png圖片無(wú)法使用,可能是保存的姿勢(shì)、方法不對(duì)。用這個(gè)方法親測(cè)可行。
微信小程序碼的生成及保存到阿里云oss
官方文檔:獲取二維碼
這里使用其中的接口B:適用于需要的碼數(shù)量極多的業(yè)務(wù)場(chǎng)景(無(wú)限次數(shù))
參數(shù)按照接口說(shuō)明來(lái)傳,請(qǐng)求成功的話接口會(huì)返回輸入流,這里將輸入流上傳至阿里云OSS
以下是部分JAVA代碼(有問(wèn)題可以留言):
public class UploadTest { @Test public void upload(){ try { URL url = new URL("https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=你的access_token"); HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection(); httpURLConnection.setRequestMethod("POST");// 提交模式 // conn.setConnectTimeout(10000);//連接超時(shí) 單位毫秒 // conn.setReadTimeout(2000);//讀取超時(shí) 單位毫秒 // 發(fā)送POST請(qǐng)求必須設(shè)置如下兩行 httpURLConnection.setDoOutput(true); httpURLConnection.setDoInput(true); // 獲取URLConnection對(duì)象對(duì)應(yīng)的輸出流 PrintWriter printWriter = new PrintWriter(httpURLConnection.getOutputStream()); // 發(fā)送請(qǐng)求參數(shù) JSONObject paramJson = new JSONObject(); paramJson.put("scene", "p=1234567890"); paramJson.put("page", "pages/index/index"); paramJson.put("width", 430); paramJson.put("auto_color", true); /** * line_color生效 * paramJson.put("auto_color", false); * JSONObject lineColor = new JSONObject(); * lineColor.put("r", 0); * lineColor.put("g", 0); * lineColor.put("b", 0); * paramJson.put("line_color", lineColor); * */ printWriter.write(paramJson.toString()); // flush輸出流的緩沖 printWriter.flush(); //開(kāi)始獲取數(shù)據(jù) //注意 這里上傳時(shí)文件大小用 HttpResponse.getEntity().getContentLength() //用 is.available()生成圖片會(huì)不全,具體參考InputStream.available AliyunOSSClientUtil.uploadAcode2OSS(httpURLConnection.getInputStream(),"app/demo.png", httpURLConnection.getContentLengthLong()) ; } catch (Exception e) { e.printStackTrace(); } } }
阿里云oss工具類
import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.net.URL; import java.util.Date; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.multipart.MultipartFile; import com.aliyun.oss.OSSClient; import com.aliyun.oss.model.Bucket; import com.aliyun.oss.model.OSSObject; import com.aliyun.oss.model.ObjectMetadata; import com.aliyun.oss.model.PutObjectResult; /** * * @ClassName: AliyunOSSClientUtil * @Description: 阿里云OSS上傳文件模塊 * @author 張日天 * @date 2018年7月02日 下午23:01:46 * */ public class AliyunOSSClientUtil { private static Logger logger = LoggerFactory .getLogger(AliyunOSSClientUtil.class); // 阿里云API的內(nèi)或外網(wǎng)域名 private static String ENDPOINT; // 阿里云API的密鑰Access Key ID private static String ACCESS_KEY_ID; // 阿里云API的密鑰Access Key Secret private static String ACCESS_KEY_SECRET; // 阿里云API的bucket名稱 private static String BACKET_NAME; // private static String FORMATS = OSSClientConstants.FORMATS; static { ENDPOINT = SysPropUtils.getPropValue("aliyun.endPoint", "properties/aliyun.properties"); ACCESS_KEY_ID = SysPropUtils.getPropValue("aliyun.accessKeyId", "properties/aliyun.properties"); ACCESS_KEY_SECRET = SysPropUtils.getPropValue("aliyun.accessKeySecret", "properties/aliyun.properties"); BACKET_NAME = SysPropUtils.getPropValue("aliyun.bucketName", "properties/aliyun.properties"); } /** * 上傳文件 至 OSS * * @param ossClient * @param is * @param bucketName * @param remotePath * @return */ public static String[] uploadObject2OSS(InputStream is, String remotePath) { String resultStr = null; String[] fo = new String[] { "", "" }; try { long start = new Date().getTime(); OSSClient ossClient = getOSSClient(); // 文件大小 // Long fileSize = file.length(); // 創(chuàng)建上傳Object的Metadata ObjectMetadata metadata = new ObjectMetadata(); // 上傳的文件的長(zhǎng)度 //metadata.setContentLength(is.available()); //System.out.println(is.available()); // 指定該Object被下載時(shí)的網(wǎng)頁(yè)的緩存行為 metadata.setCacheControl("no-cache"); // 指定該Object下設(shè)置Header metadata.setHeader("Pragma", "no-cache"); // 指定該Object被下載時(shí)的內(nèi)容編碼格式 metadata.setContentEncoding("utf-8"); // 文件的MIME,定義文件的類型及網(wǎng)頁(yè)編碼,決定瀏覽器將以什么形式、什么編碼讀取文件。如果用戶沒(méi)有指定則根據(jù)Key或文件名的擴(kuò)展名生成, // 如果沒(méi)有擴(kuò)展名則填默認(rèn)值application/octet-stream // metadata.setContentType(getContentType(fileName)); // 指定該Object被下載時(shí)的名稱(指示MINME用戶代理如何顯示附加的文件,打開(kāi)或下載,及文件名稱) // metadata.setContentDisposition("filename/filesize=" + fileName + // "/" + fileSize + "Byte."); // 上傳文件 (上傳文件流的形式) PutObjectResult putResult = ossClient.putObject(BACKET_NAME, remotePath, is, metadata); // 解析結(jié)果 resultStr = putResult.getETag(); fo[1] = remotePath; fo[0] = resultStr; // 文件名 logger.info("文件上傳成功,上傳耗時(shí):"+(new Date().getTime()-start)+"ms,路徑為:" + remotePath); ossClient.shutdown(); } catch (Exception e) { e.printStackTrace(); logger.error("上傳阿里云OSS服務(wù)器異常." + e.getMessage(), e); } return fo; } /** * 上傳微信小程序碼 至 OSS * @param is * @param remotePath * @param length * @return */ public static String[] uploadAcode2OSS(InputStream is, String remotePath,long length) { String resultStr = null; String[] fo = new String[] { "", "" }; try { long start = new Date().getTime(); OSSClient ossClient = getOSSClient(); // 文件大小 // Long fileSize = file.length(); // 創(chuàng)建上傳Object的Metadata ObjectMetadata metadata = new ObjectMetadata(); // 上傳的文件的長(zhǎng)度 metadata.setContentLength(length); //System.out.println(is.available()); // 指定該Object被下載時(shí)的網(wǎng)頁(yè)的緩存行為 metadata.setCacheControl("no-cache"); // 指定該Object下設(shè)置Header metadata.setHeader("Pragma", "no-cache"); // 指定該Object被下載時(shí)的內(nèi)容編碼格式 metadata.setContentEncoding("utf-8"); // 文件的MIME,定義文件的類型及網(wǎng)頁(yè)編碼,決定瀏覽器將以什么形式、什么編碼讀取文件。如果用戶沒(méi)有指定則根據(jù)Key或文件名的擴(kuò)展名生成, // 如果沒(méi)有擴(kuò)展名則填默認(rèn)值application/octet-stream // metadata.setContentType(getContentType(fileName)); // 指定該Object被下載時(shí)的名稱(指示MINME用戶代理如何顯示附加的文件,打開(kāi)或下載,及文件名稱) // metadata.setContentDisposition("filename/filesize=" + fileName + // "/" + fileSize + "Byte."); // 上傳文件 (上傳文件流的形式) PutObjectResult putResult = ossClient.putObject(BACKET_NAME, remotePath, is, metadata); // 解析結(jié)果 resultStr = putResult.getETag(); fo[1] = remotePath; fo[0] = resultStr; // 文件名 logger.info("文件上傳成功,上傳耗時(shí):"+(new Date().getTime()-start)+"ms,路徑為:" + remotePath); ossClient.shutdown(); } catch (Exception e) { e.printStackTrace(); logger.error("上傳阿里云OSS服務(wù)器異常." + e.getMessage(), e); } return fo; } /** * 上傳文件 至 OSS * * @param ossClient * @param is * @param bucketName * @param remotePath * @return */ public static String[] uploadObject2OSS(OSSClient ossClient, InputStream is, String bucketName, String remotePath) { String resultStr = null; String[] fo = new String[] { "", "" }; try { // 文件大小 // Long fileSize = file.length(); // 創(chuàng)建上傳Object的Metadata ObjectMetadata metadata = new ObjectMetadata(); // 上傳的文件的長(zhǎng)度 metadata.setContentLength(is.available()); // 指定該Object被下載時(shí)的網(wǎng)頁(yè)的緩存行為 metadata.setCacheControl("no-cache"); // 指定該Object下設(shè)置Header metadata.setHeader("Pragma", "no-cache"); // 指定該Object被下載時(shí)的內(nèi)容編碼格式 metadata.setContentEncoding("utf-8"); // 文件的MIME,定義文件的類型及網(wǎng)頁(yè)編碼,決定瀏覽器將以什么形式、什么編碼讀取文件。如果用戶沒(méi)有指定則根據(jù)Key或文件名的擴(kuò)展名生成, // 如果沒(méi)有擴(kuò)展名則填默認(rèn)值application/octet-stream // metadata.setContentType("image/jpeg"); // 指定該Object被下載時(shí)的名稱(指示MINME用戶代理如何顯示附加的文件,打開(kāi)或下載,及文件名稱) // metadata.setContentDisposition("filename/filesize=" + fileName + // "/" + fileSize + "Byte."); // 上傳文件 (上傳文件流的形式) PutObjectResult putResult = ossClient.putObject(bucketName, remotePath, is, metadata); // 解析結(jié)果 resultStr = putResult.getETag(); fo[1] = remotePath; fo[0] = resultStr; // 文件名 logger.info("文件上傳成功,路徑為:" + remotePath); ossClient.shutdown(); } catch (Exception e) { e.printStackTrace(); logger.error("上傳阿里云OSS服務(wù)器異常." + e.getMessage(), e); } return fo; } /** * 上傳圖片至OSS 文件流 * * @param ossClient * oss連接 * @param file * 上傳文件(文件全路徑如:D:imagecake.jpg) * @param bucketName * 存儲(chǔ)空間 * @param folder * 模擬文件夾名 如"qj_nanjing/" * @return String 返回的唯一MD5數(shù)字簽名 */ public static String[] uploadObject2OSS(OSSClient ossClient, File file, String bucketName, String folder) { String resultStr = null; String[] fo = new String[] { "", "" }; try { // 以輸入流的形式上傳文件 InputStream is = new FileInputStream(file); // 文件名 String timefile = FORMATS; String fileName = file.getName(); fileName = timefile + fileName.substring(fileName.lastIndexOf(".")); logger.info("上傳到路徑" + folder + fileName); // 文件大小 Long fileSize = file.length(); // 創(chuàng)建上傳Object的Metadata ObjectMetadata metadata = new ObjectMetadata(); // 上傳的文件的長(zhǎng)度 metadata.setContentLength(is.available()); // 指定該Object被下載時(shí)的網(wǎng)頁(yè)的緩存行為 metadata.setCacheControl("no-cache"); // 指定該Object下設(shè)置Header metadata.setHeader("Pragma", "no-cache"); // 指定該Object被下載時(shí)的內(nèi)容編碼格式 metadata.setContentEncoding("utf-8"); // 文件的MIME,定義文件的類型及網(wǎng)頁(yè)編碼,決定瀏覽器將以什么形式、什么編碼讀取文件。如果用戶沒(méi)有指定則根據(jù)Key或文件名的擴(kuò)展名生成, // 如果沒(méi)有擴(kuò)展名則填默認(rèn)值application/octet-stream metadata.setContentType(getContentType(fileName)); // 指定該Object被下載時(shí)的名稱(指示MINME用戶代理如何顯示附加的文件,打開(kāi)或下載,及文件名稱) metadata.setContentDisposition("filename/filesize=" + fileName + "/" + fileSize + "Byte."); // 上傳文件 (上傳文件流的形式) PutObjectResult putResult = ossClient.putObject(bucketName, folder + fileName, is, metadata); // 解析結(jié)果 resultStr = putResult.getETag(); fo[1] = folder + fileName; fo[0] = resultStr; ossClient.shutdown(); } catch (Exception e) { e.printStackTrace(); logger.error("上傳阿里云OSS服務(wù)器異常." + e.getMessage(), e); } return fo; } /** * 獲取阿里云OSS客戶端對(duì)象 * * @return ossClient */ public static OSSClient getOSSClient() { return new OSSClient(ENDPOINT, ACCESS_KEY_ID, ACCESS_KEY_SECRET); } /** * 獲取阿里云OSS客戶端對(duì)象 * * @param endPoint * @param accessKeyId * @param accessKeySecret * @return */ public static OSSClient getOSSClient(String endPoint, String accessKeyId, String accessKeySecret) { return new OSSClient(endPoint, accessKeyId, accessKeySecret); } /** * 創(chuàng)建存儲(chǔ)空間 * * @param ossClient * OSS連接 * @param bucketName * 存儲(chǔ)空間 * @return */ public static String createBucket(OSSClient ossClient, String bucketName) { // 存儲(chǔ)空間 String bucketNames = bucketName; if (!ossClient.doesBucketExist(bucketName)) { // 創(chuàng)建存儲(chǔ)空間 Bucket bucket = ossClient.createBucket(bucketName); logger.info("創(chuàng)建存儲(chǔ)空間成功"); return bucket.getName(); } else { logger.info("名為 " + bucketName + " 存儲(chǔ)空間已經(jīng)存在"); } return bucketNames; } /** * 刪除存儲(chǔ)空間buckName * * @param ossClient * oss對(duì)象 * @param bucketName * 存儲(chǔ)空間 */ public static void deleteBucket(OSSClient ossClient, String bucketName) { ossClient.deleteBucket(bucketName); logger.info("刪除" + bucketName + "Bucket成功"); } /** * 刪除存儲(chǔ)空間buckName * * @param bucketName * 存儲(chǔ)空間 */ public static void deleteBucket(String bucketName) { getOSSClient().deleteBucket(bucketName); logger.info("刪除" + bucketName + " Bucket成功"); } /** * 創(chuàng)建模擬文件夾 * * @param ossClient * oss連接 * @param bucketName * 存儲(chǔ)空間 * @param folder * 模擬文件夾名如"anhui/hefei/" * @return 文件夾名 */ public static String createFolder(OSSClient ossClient, String bucketName, String folder) { // 文件夾名 String keySuffixWithSlash = folder; // 判斷文件夾是否存在,不存在則創(chuàng)建 if (!ossClient.doesObjectExist(bucketName, keySuffixWithSlash)) { // 創(chuàng)建文件夾 ossClient.putObject(bucketName, keySuffixWithSlash, new ByteArrayInputStream(new byte[0])); logger.info("創(chuàng)建文件夾成功"); // 得到文件夾名 OSSObject object = ossClient.getObject(bucketName, keySuffixWithSlash); String fileDir = object.getKey(); return fileDir; } return keySuffixWithSlash; } /** * 根據(jù)key刪除OSS服務(wù)器上的文件 * * @param ossClient * oss連接 * @param bucketName * 存儲(chǔ)空間 * @param folder * 模擬文件夾名 如"anhui/hefei/" * @param key * Bucket下的文件的路徑名+文件名 如:"demo.jpg" */ public static void deleteFile(OSSClient ossClient, String bucketName, String folder, String key) { ossClient.deleteObject(bucketName, folder + key); logger.info("刪除" + bucketName + "下的文件" + folder + key + "成功"); } /** * 根據(jù)key刪除OSS服務(wù)器上的文件 * * @param ossClient * oss連接 * @param bucketName * 存儲(chǔ)空間 * @param filePath * Bucket下的文件的路徑名+文件名 如:"beijing/demo.jpg" */ public static void deleteFile(OSSClient ossClient, String bucketName, String filePath) { ossClient.deleteObject(bucketName, filePath); logger.info("刪除" + bucketName + "下的文件 " + filePath + " 成功"); } /** * 獲得url鏈接 * * @param key * @return */ public static String getUrl(OSSClient ossClient, String bucketName, String fileName) { // 設(shè)置URL過(guò)期時(shí)間為10年 3600l*60*24*365*10 Date expiration = new Date(new Date().getTime() + 3600l * 1000 * 24 * 365 * 10); // 生成URL // ossClient.gen URL url = ossClient.generatePresignedUrl(bucketName, fileName, expiration); if (url != null) { return url.toString(); } return "獲網(wǎng)址路徑出錯(cuò)"; } /** * 通過(guò)文件名判斷并獲取OSS服務(wù)文件上傳時(shí)文件的contentType * * @param fileName * 文件名 * @return 文件的contentType */ public static String getContentType(String fileName) { // 文件的后綴名 String fileExtension = fileName.substring(fileName.lastIndexOf(".")); if (".bmp".equalsIgnoreCase(fileExtension)) { return "image/bmp"; } if (".gif".equalsIgnoreCase(fileExtension)) { return "image/gif"; } if (".jpeg".equalsIgnoreCase(fileExtension) || ".jpg".equalsIgnoreCase(fileExtension) || ".png".equalsIgnoreCase(fileExtension)) { return "image/jpeg"; } if (".html".equalsIgnoreCase(fileExtension)) { return "text/html"; } if (".txt".equalsIgnoreCase(fileExtension)) { return "text/plain"; } if (".vsd".equalsIgnoreCase(fileExtension)) { return "application/vnd.visio"; } if (".ppt".equalsIgnoreCase(fileExtension) || "pptx".equalsIgnoreCase(fileExtension)) { return "application/vnd.ms-powerpoint"; } if (".doc".equalsIgnoreCase(fileExtension) || "docx".equalsIgnoreCase(fileExtension)) { return "application/msword"; } if (".xml".equalsIgnoreCase(fileExtension)) { return "text/xml"; } if (".mp4".equalsIgnoreCase(fileExtension)) { return "video/mp4"; } // 默認(rèn)返回類型 return "image/jpeg"; } }
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持服務(wù)器之家。
原文鏈接:https://zhengkai.blog.csdn.net/article/details/107586571