获取小程序带参二维码并保存到本地
业务场景
下载并保存带参数的小程序二维码,用户直接扫描带参二维码就进入小程序,自动根据参数完成部分业务。这个时候就需要用到微信小程序提供的二维码接口 wxacode.getUnlimited ,官方文档地址
网上介绍的很多方法有些过时了,有些不科学,调用起来不方便,所以自己也总结了一份出来。虽然很久之前直接用jfinal+jfinal-wx写的,几句代码就搞定了,但是最近需要迁移项目到SpringBoot2,所以就踩了个坑顺便总结一下,确实找了大半天。
POSTMAN调试
如果一个借口postman都调用不通,那就更别说用java代码去写了,调用接口第一步就是熟悉接口请求方式 method、请求格式 Content-Type和接口参数 Body/Param以及返回的内容,POSTMAN调用参数过程
url https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token={{access_token}} json raw { "scene":"EGAG-SAL**-******"}
WxUtil封装:下载带参数的小程序二维码
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"); } /** * 下载带参数的小程序二维码 * 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 带过去小程序的参数,一般为你的业务参数,建议是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);//连接超时 单位毫秒 httpURLConnection.setReadTimeout(10000);//读取超时 单位毫秒 // 发送POST请求必须设置如下两行 httpURLConnection.setDoOutput(true); httpURLConnection.setDoInput(true); // 获取URLConnection对象对应的输出流 PrintWriter printWriter = new PrintWriter(httpURLConnection.getOutputStream()); printWriter.write(JSON.toJSONString(paramMap)); // flush输出流的缓冲 printWriter.flush(); //开始获取数据 BufferedInputStream bis = new BufferedInputStream(httpURLConnection.getInputStream()); OutputStream os = new FileOutputStream(new File(imgFilePath)); int len; //设置缓冲写入 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调用
传入保存的 path地址 和 scene参数
WxUtil.downloadMiniCode(storageService.getPathString(),certCompany.getCertNumber());
下载验证
之前试过很多方法就是保存后的png图片无法使用,可能是保存的姿势、方法不对。用这个方法亲测可行。
微信小程序码的生成及保存到阿里云oss
官方文档:获取二维码
这里使用其中的接口B:适用于需要的码数量极多的业务场景(无限次数)
参数按照接口说明来传,请求成功的话接口会返回输入流,这里将输入流上传至阿里云OSS
以下是部分JAVA代码(有问题可以留言):
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);//连接超时 单位毫秒 // conn.setReadTimeout(2000);//读取超时 单位毫秒 // 发送POST请求必须设置如下两行 httpURLConnection.setDoOutput(true); httpURLConnection.setDoInput(true); // 获取URLConnection对象对应的输出流 PrintWriter printWriter = new PrintWriter(httpURLConnection.getOutputStream()); // 发送请求参数 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(); //开始获取数据 //注意 这里上传时文件大小用 HttpResponse.getEntity().getContentLength() //用 is.available()生成图片会不全,具体参考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的内或外网域名 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(); // 创建上传Object的Metadata ObjectMetadata metadata = new ObjectMetadata(); // 上传的文件的长度 //metadata.setContentLength(is.available()); //System.out.println(is.available()); // 指定该Object被下载时的网页的缓存行为 metadata.setCacheControl("no-cache"); // 指定该Object下设置Header metadata.setHeader("Pragma", "no-cache"); // 指定该Object被下载时的内容编码格式 metadata.setContentEncoding("utf-8"); // 文件的MIME,定义文件的类型及网页编码,决定浏览器将以什么形式、什么编码读取文件。如果用户没有指定则根据Key或文件名的扩展名生成, // 如果没有扩展名则填默认值application/octet-stream // metadata.setContentType(getContentType(fileName)); // 指定该Object被下载时的名称(指示MINME用户代理如何显示附加的文件,打开或下载,及文件名称) // metadata.setContentDisposition("filename/filesize=" + fileName + // "/" + fileSize + "Byte."); // 上传文件 (上传文件流的形式) PutObjectResult putResult = ossClient.putObject(BACKET_NAME, remotePath, is, metadata); // 解析结果 resultStr = putResult.getETag(); fo[1] = remotePath; fo[0] = resultStr; // 文件名 logger.info("文件上传成功,上传耗时:"+(new Date().getTime()-start)+"ms,路径为:" + remotePath); ossClient.shutdown(); } catch (Exception e) { e.printStackTrace(); logger.error("上传阿里云OSS服务器异常." + 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(); // 创建上传Object的Metadata ObjectMetadata metadata = new ObjectMetadata(); // 上传的文件的长度 metadata.setContentLength(length); //System.out.println(is.available()); // 指定该Object被下载时的网页的缓存行为 metadata.setCacheControl("no-cache"); // 指定该Object下设置Header metadata.setHeader("Pragma", "no-cache"); // 指定该Object被下载时的内容编码格式 metadata.setContentEncoding("utf-8"); // 文件的MIME,定义文件的类型及网页编码,决定浏览器将以什么形式、什么编码读取文件。如果用户没有指定则根据Key或文件名的扩展名生成, // 如果没有扩展名则填默认值application/octet-stream // metadata.setContentType(getContentType(fileName)); // 指定该Object被下载时的名称(指示MINME用户代理如何显示附加的文件,打开或下载,及文件名称) // metadata.setContentDisposition("filename/filesize=" + fileName + // "/" + fileSize + "Byte."); // 上传文件 (上传文件流的形式) PutObjectResult putResult = ossClient.putObject(BACKET_NAME, remotePath, is, metadata); // 解析结果 resultStr = putResult.getETag(); fo[1] = remotePath; fo[0] = resultStr; // 文件名 logger.info("文件上传成功,上传耗时:"+(new Date().getTime()-start)+"ms,路径为:" + remotePath); ossClient.shutdown(); } catch (Exception e) { e.printStackTrace(); logger.error("上传阿里云OSS服务器异常." + 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(); // 创建上传Object的Metadata ObjectMetadata metadata = new ObjectMetadata(); // 上传的文件的长度 metadata.setContentLength(is.available()); // 指定该Object被下载时的网页的缓存行为 metadata.setCacheControl("no-cache"); // 指定该Object下设置Header metadata.setHeader("Pragma", "no-cache"); // 指定该Object被下载时的内容编码格式 metadata.setContentEncoding("utf-8"); // 文件的MIME,定义文件的类型及网页编码,决定浏览器将以什么形式、什么编码读取文件。如果用户没有指定则根据Key或文件名的扩展名生成, // 如果没有扩展名则填默认值application/octet-stream // metadata.setContentType("image/jpeg"); // 指定该Object被下载时的名称(指示MINME用户代理如何显示附加的文件,打开或下载,及文件名称) // metadata.setContentDisposition("filename/filesize=" + fileName + // "/" + fileSize + "Byte."); // 上传文件 (上传文件流的形式) PutObjectResult putResult = ossClient.putObject(bucketName, remotePath, is, metadata); // 解析结果 resultStr = putResult.getETag(); fo[1] = remotePath; fo[0] = resultStr; // 文件名 logger.info("文件上传成功,路径为:" + remotePath); ossClient.shutdown(); } catch (Exception e) { e.printStackTrace(); logger.error("上传阿里云OSS服务器异常." + e.getMessage(), e); } return fo; } /** * 上传图片至OSS 文件流 * * @param ossClient * oss连接 * @param file * 上传文件(文件全路径如:D:imagecake.jpg) * @param bucketName * 存储空间 * @param folder * 模拟文件夹名 如"qj_nanjing/" * @return String 返回的唯一MD5数字签名 */ 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(); // 创建上传Object的Metadata ObjectMetadata metadata = new ObjectMetadata(); // 上传的文件的长度 metadata.setContentLength(is.available()); // 指定该Object被下载时的网页的缓存行为 metadata.setCacheControl("no-cache"); // 指定该Object下设置Header metadata.setHeader("Pragma", "no-cache"); // 指定该Object被下载时的内容编码格式 metadata.setContentEncoding("utf-8"); // 文件的MIME,定义文件的类型及网页编码,决定浏览器将以什么形式、什么编码读取文件。如果用户没有指定则根据Key或文件名的扩展名生成, // 如果没有扩展名则填默认值application/octet-stream metadata.setContentType(getContentType(fileName)); // 指定该Object被下载时的名称(指示MINME用户代理如何显示附加的文件,打开或下载,及文件名称) metadata.setContentDisposition("filename/filesize=" + fileName + "/" + fileSize + "Byte."); // 上传文件 (上传文件流的形式) PutObjectResult putResult = ossClient.putObject(bucketName, folder + fileName, is, metadata); // 解析结果 resultStr = putResult.getETag(); fo[1] = folder + fileName; fo[0] = resultStr; ossClient.shutdown(); } catch (Exception e) { e.printStackTrace(); logger.error("上传阿里云OSS服务器异常." + e.getMessage(), e); } return fo; } /** * 获取阿里云OSS客户端对象 * * @return ossClient */ public static OSSClient getOSSClient() { return new OSSClient(ENDPOINT, ACCESS_KEY_ID, ACCESS_KEY_SECRET); } /** * 获取阿里云OSS客户端对象 * * @param endPoint * @param accessKeyId * @param accessKeySecret * @return */ public static OSSClient getOSSClient(String endPoint, String accessKeyId, String accessKeySecret) { return new OSSClient(endPoint, accessKeyId, accessKeySecret); } /** * 创建存储空间 * * @param ossClient * OSS连接 * @param bucketName * 存储空间 * @return */ public static String createBucket(OSSClient ossClient, String bucketName) { // 存储空间 String bucketNames = bucketName; if (!ossClient.doesBucketExist(bucketName)) { // 创建存储空间 Bucket bucket = ossClient.createBucket(bucketName); logger.info("创建存储空间成功"); return bucket.getName(); } else { logger.info("名为 " + bucketName + " 存储空间已经存在"); } return bucketNames; } /** * 删除存储空间buckName * * @param ossClient * oss对象 * @param bucketName * 存储空间 */ public static void deleteBucket(OSSClient ossClient, String bucketName) { ossClient.deleteBucket(bucketName); logger.info("删除" + bucketName + "Bucket成功"); } /** * 删除存储空间buckName * * @param bucketName * 存储空间 */ public static void deleteBucket(String bucketName) { getOSSClient().deleteBucket(bucketName); logger.info("删除" + bucketName + " Bucket成功"); } /** * 创建模拟文件夹 * * @param ossClient * oss连接 * @param bucketName * 存储空间 * @param folder * 模拟文件夹名如"anhui/hefei/" * @return 文件夹名 */ public static String createFolder(OSSClient ossClient, String bucketName, String folder) { // 文件夹名 String keySuffixWithSlash = folder; // 判断文件夹是否存在,不存在则创建 if (!ossClient.doesObjectExist(bucketName, keySuffixWithSlash)) { // 创建文件夹 ossClient.putObject(bucketName, keySuffixWithSlash, new ByteArrayInputStream(new byte[0])); logger.info("创建文件夹成功"); // 得到文件夹名 OSSObject object = ossClient.getObject(bucketName, keySuffixWithSlash); String fileDir = object.getKey(); return fileDir; } return keySuffixWithSlash; } /** * 根据key删除OSS服务器上的文件 * * @param ossClient * oss连接 * @param bucketName * 存储空间 * @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 + "成功"); } /** * 根据key删除OSS服务器上的文件 * * @param ossClient * oss连接 * @param bucketName * 存储空间 * @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) { // 设置URL过期时间为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 "获网址路径出错"; } /** * 通过文件名判断并获取OSS服务文件上传时文件的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"; } // 默认返回类型 return "image/jpeg"; } }
以上为个人经验,希望能给大家一个参考,也希望大家多多支持服务器之家。
原文链接:https://zhengkai.blog.csdn.net/article/details/107586571