目录
- 事件起因
- 环境和工具
- 操作过程
- 解决办法
- 遇到的一点问题
- 结束语
事件起因
在开发一个关于微信小程序的过程中,有一个这样的需求,要求生成微信小程序的太阳码,然而这个东西的请求方式我们是这样的:我作为后端服务去请求这个太阳码的二维码,然后将获取到的太阳码二维码的图片返回给小程序端进行接收,然后小程序端进行一个展示
原本以为他们小程序端直接去请求那个图片就行了,但是最后商讨下来还是由我们后端去请求这个太阳码,然后返回给前端去展示
过程中就遇到一些数据请求和转换的问题,就先在这儿记录一下,以便后来者踩坑
环境和工具
java jdk1.8
操作过程
先是接口层,最后完成的版本是这样:
@PostMapping("/getSunQRCode")@ApiOperation(value = "生成太阳码-获取小程序不限制的QR码", notes = "生成太阳码-获取小程序不限制的QR码")public Result<SunQRCodeVo> getUnlimitedQRCode(@RequestBody UnlimitedQRCodeDTO unlimitedQRCodeDTO, HttpServletResponse response) throws IOException {return newUserService.getUnlimitedQRCode(unlimitedQRCodeDTO);}
大致解释j就是一个post请求,然后这个请求返回的是一个封装后的实体的Result,然后内部的实体是一个字符串(之所以是字符串,是因为最后图片以base64编码的格式返回给前端的,不然就得以流的形式返回)
然后就是具体的实现层的操作,大致操作如下:
通过微信的接口请求太阳码 ----》将拿到的太阳码转换为对应的图片格式(微信那边默认返回的是jpeg格式,因为前端的要求,需要转换为png的格式,然后再转换为对应的base64的字符串,然后再返回给前端) ----》转换为base64的格式,封装实体返回给前端。
解决办法
最后的一个实现层的代码版本(这个代码有个优势:就是可以根据微信接口的返回内容,如果请求正确,微信的这个接口它会直接返回buffer的图片,但如果请求有问题,它的返回内容又是一个json,所以这种情况可以根据返回的内容去判断,然后再具体去考虑如何接收)
import org.springframework.web.client.RestTemplate;@Service("NewUserService")
public class NewUserServiceImpl extends BaseServiceImpl<NewUserDao, NewUserDO, BaseDTO> implements NewUserService {private static RestTemplate restTemplate;public static RestTemplate getRestTemplate() {if (null == restTemplate) {synchronized (RestTemplate.class) {if (null == restTemplate) {restTemplate = new RestTemplate();}}}return restTemplate;}/*** 获取微信小程序的小程序码*/@Overridepublic Result<SunQRCodeVo> getUnlimitedQRCode(UnlimitedQRCodeDTO unlimitedQRCodeDTO) throws IOException {Result<SunQRCodeVo> result = new Result<>();//先根据配置的appid等信息获取到tokenString accessToken = this.getAccessToken().getAccess_token();String url = "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=" + accessToken;// byte[] qrCodeVo = getRestTemplate().postForObject(url, paramMap, byte[].class,
// ContentType.APPLICATION_JSON);CloseableHttpClient client = HttpClients.createDefault();HttpPost request = new HttpPost(url);request.setHeader("Content-Type", "application/json");// 将json参数作为请求体发送JSONObject jsonParam = new JSONObject();jsonParam.put("scene",unlimitedQRCodeDTO.getScene());jsonParam.put("env_version",unlimitedQRCodeDTO.getEnv_version());jsonParam.put("page",unlimitedQRCodeDTO.getPage());jsonParam.put("width",unlimitedQRCodeDTO.getWidth()==null?"280":unlimitedQRCodeDTO.getWidth());StringEntity entity = new StringEntity(jsonParam.toString(), ContentType.APPLICATION_JSON);request.setEntity(entity);CloseableHttpResponse response = client.execute(request);// 获取响应头中的Content-Type字段Header contentTypeHeader = response.getFirstHeader("Content-Type");if (contentTypeHeader != null && contentTypeHeader.getValue().contains("image/jpeg")) {// 如果返回值是jpeg类型,以输入流的形式读取InputStream is = response.getEntity().getContent();//字节数组的输出流,用户辅助图片在流之间的格式转换ByteArrayOutputStream outputStream = new ByteArrayOutputStream();//将请求获取到的输入流 使用ImageIO转换为bufferedImage后以png的格式写入输出流,然后将输出流转换为字节数组,后面将字节数组转换为base64编码的字符串BufferedImage image = ImageIO.read(is);ImageIO.write(image, "png", outputStream);byte[] pngBytes = outputStream.toByteArray();outputStream.close();/* 该段注释代码 作用是byte[] buffer = new byte[1024];int length;while ((length = is.read(buffer)) != -1) {outputStream.write(buffer, 0, length);}byte[] data = outputStream.toByteArray();*///关闭创建的输入流inputStreamis.close();SunQRCodeVo sunQRCodeVo = new SunQRCodeVo();
// sunQRCodeVo.setData(data);// 二进制数据字节数组 转base64编码的字符串sunQRCodeVo.setBase64buffer(Base64.getEncoder().encodeToString(pngBytes));sunQRCodeVo.setErrcode(0);result.setResult(sunQRCodeVo);return result;} else {// 如果返回值是json类型,则解析json数据BufferedReader reader = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));String line;StringBuilder sb = new StringBuilder();while ((line = reader.readLine()) != null) {sb.append(line);}reader.close();String json = sb.toString();JSONObject obj = new JSONObject(json);SunQRCodeVo sunQRCodeVo = new SunQRCodeVo();sunQRCodeVo.setErrcode(obj.getInt("errcode"));sunQRCodeVo.setErrmsg(obj.getString("errmsg"));return result.error500(obj.getString("errmsg"));}}//访问微信的服务器找到指定的小程序,获取登录public AccessTokenVo getAccessToken() {String url ="https://api.weixin.qq.com/cgi-bin/token?grant_type=" + GRANT_TYPE + "&appid=" + APPID + "&secret=" + SECRET;return getRestTemplate().getForObject(url, AccessTokenVo.class);}}
原本只贴了一个实现的方法,后面发现可能那个RestTemplate可能看不懂,就又给这个方法的import的包和在该实现层的具体实现给附加上了,上面代码中还有几个实体结构也在下面:
用于获取AccessTokenVo 的实体类:
@Data
public class AccessTokenVo {private String access_token;private Integer expires_in;/*** @description:* -1.系统繁忙,此时请开发者稍候再试* 0.请求成功* 40001.AppSecret 错误或者 AppSecret 不属于这个小程序,请开发者确认 AppSecret 的正确性* 40002.请确保 grant_type 字段值为 client_credential* 40013.不合法的 AppID,请开发者检查 AppID 的正确性,避免异常字符,注意大小写* @param: @param null* @return:* @author: liuanmin* @date: 2022/4/29*/private Integer errcode;private String errmsg;
}
用于封装返回请求的SunQRCode实体:
@Data
public class SunQRCodeVo {/*** 二进制流*/private byte[] data;/*** 图片二进制流转base64编码的字符串*/private String base64buffer;/*** 小程序返回的错误码*/private Integer errcode;/*** 小程序返回的错误信息*/private String errmsg;
}
遇到的一点问题
上面有一小段我注释里的代码,是之前遇到的一点问题,但是经过排查后发现的问题所在如下:
拿到请求的内容后我使用new一个字节数组的大小刚好与返回获取的流一样的大小去读取这个InputStream,结果读出来只有一部分,没有读取完,如下
在对这部分进行验证时:
inputstream的大小:
通过available方法获取到的大小只有8010,当前是win11系统
关于这个获取到的大小的问题,问了一下"万能"的chatgpt
顺便说一下,这段时间以来使用chatgpt的一个感受:
能用,确实也挺智能,在数据库的管理和代码的提示上能提供不小的帮助,但也有一些小毛病,比如说 突然崩了,有时候对话它逻辑跟不上,有时候反应慢,有时候不能根据前面指定内容进行继续回答,还有就是回答内容过长时会断,让它继续输出时,中间会缺失部分内容;还有部分问题太细太专业,它也回答不了。
你描述得越准确,它的回答就越符合你的期望,将你的背景,使用情况描述得越清楚,就越贴近你想要的结果。
结束语
若是对你有所帮助的话,希望能获得你的 点赞、评论、收藏,这将是对我很大的鼓励!!! 这对我真的很重要!!!
蟹蟹٩(‘ω’)و