目录
- 事件起因
- 环境和工具
- 操作过程
- 解决办法
- 遇到的一点问题
- 结束语
事件起因
在开发一个关于微信小程序的过程中,有一个这样的需求,要求生成微信小程序的太阳码,然而这个东西的请求方式我们是这样的:我作为后端服务去请求这个太阳码的二维码,然后将获取到的太阳码二维码的图片返回给小程序端进行接收,然后小程序端进行一个展示
原本以为他们小程序端直接去请求那个图片就行了,但是最后商讨下来还是由我们后端去请求这个太阳码,然后返回给前端去展示
过程中就遇到一些数据请求和转换的问题,就先在这儿记录一下,以便后来者踩坑
环境和工具
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;
}
/**
* 获取微信小程序的小程序码
*/
@Override
public Result<SunQRCodeVo> getUnlimitedQRCode(UnlimitedQRCodeDTO unlimitedQRCodeDTO) throws IOException {
Result<SunQRCodeVo> result = new Result<>();
//先根据配置的appid等信息获取到token
String 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();
*/
//关闭创建的输入流inputStream
is.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的一个感受:
能用,确实也挺智能,在数据库的管理和代码的提示上能提供不小的帮助,但也有一些小毛病,比如说 突然崩了,有时候对话它逻辑跟不上,有时候反应慢,有时候不能根据前面指定内容进行继续回答,还有就是回答内容过长时会断,让它继续输出时,中间会缺失部分内容;还有部分问题太细太专业,它也回答不了。
你描述得越准确,它的回答就越符合你的期望,将你的背景,使用情况描述得越清楚,就越贴近你想要的结果。
结束语
若是对你有所帮助的话,希望能获得你的 点赞、评论、收藏,这将是对我很大的鼓励!!! 这对我真的很重要!!!
蟹蟹٩(‘ω’)و