这是我参与8月更文挑战的第5天,活动详情查看:8月更文挑战
前言
本篇文章的解决的问题是,从微信开放平台调用 API 生成二维码,并将二维码颜色转成黑白,生成业务逻辑的关系之后,再将文件上传。本片文章介绍了一个完整的流程,从二维码的生成到存储到目标服务器的整个过程,特别记录如下,以备后续查看学习以及纠正。
一、二维码生成
1.1 获取token
本次开发是针对微信开放平台的逻辑,传入appid 和 密钥 获取 token
public class AccessTokenSDK {
public static GetAccessTokenVO token(GetAccessTokenDTO getAccessTokenDTO) {
try{
String result = HttpRequest
.get(String.format(AccessTokenEnum.ACCESS_TOKEN_ENUM.getUrl(), getAccessTokenDTO.getAppid(), getAccessTokenDTO.getSecret()))
// 超时
.timeout(5000)
.execute()
.body();
ObjectMapper objectMapper = new ObjectMapper();
return objectMapper.readValue(result, GetAccessTokenVO.class);
}catch (Exception e){
e.printStackTrace();
}
return null;
}
}
复制代码
返回的 tokenvo
@Data
public class GetAccessTokenVO {
private String errcode;
private String errmsg;
private String access_token; // token
private Integer expires_in; // 失效时间
}
复制代码
封装的地址
public enum AccessTokenEnum {
/**
* 获取token
**/
ACCESS_TOKEN_ENUM("获取token", "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s"),
;
/**
* 描述
**/
private String desc;
/**
* 地址
**/
private String url;
AccessTokenEnum(String desc, String url) {
this.desc = desc;
this.url = url;
}
public String getDesc() {
return desc;
}
public String getUrl() {
return url;
}
@Override
public String toString() {
return "AccessTokenEnum{" +
"desc='" + desc + ''' +
", url='" + url + ''' +
'}';
}
}
复制代码
1.2 获取小程序码
1.2.1 创建一个二维码输入实体
/**
* 获取小程序路径二维码图片
* @author big uncle
* @date 2021/7/14 9:23
**/
@Data
public class GetWxCodeImageLimitDTO{
/**
* 该值为加密后的前端使用的参数,和前端交互的唯一字段
**/
private String scene;
/**
* 小程序页面路径
**/
private String page;
/**
* 二维码的宽度,单位 px,最小 280px,最大 1280px
**/
private Integer width;
/**
* 自动配置线条颜色,如果颜色依然是黑色,则说明不建议配置主色调,默认 false
**/
private Boolean auto_color;
/**
* auto_color 为 false 时生效,使用 rgb 设置颜色 例如 {"r":"xxx","g":"xxx","b":"xxx"} 十进制表示
**/
private LineColor line_color;
/**
* 是否需要透明底色,为 true 时,生成透明底色的小程序
**/
private Boolean is_hyaline;
@Data
public static class LineColor{
private String r;
private String g;
private String b;
}
}
复制代码
1.2.2 组装二维码信息
GetWxCodeImageLimitDTO getWxCodeImageLimitDTO = new GetWxCodeImageLimitDTO();
getWxCodeImageLimitDTO.setScene(deviceInfo.getDeviceType() + deviceInfo.getDevId() + "," + deviceInfo.getId());
getWxCodeImageLimitDTO.setPage("pages/index/quota/index");
getWxCodeImageLimitDTO.setWidth(283);
PublicDTO<GetWxCodeImageLimitDTO> publicDTO = new PublicDTO<>();
publicDTO.setToken(wxappkeyinDB);
publicDTO.setData(getWxCodeImageLimitDTO);
复制代码
1.2.3 请求生成二维码
传入实体和获取到的token,去生成二维码,返回一个文件流。
InputStream stream = ScanCodeImageSDK.getWxCodeImageLimit(publicDTO);
复制代码
/**
* 获取二维码
**/
@Slf4j
public class ScanCodeImageSDK {
static ObjectMapper objectMapper = new ObjectMapper();
/**
* 获取小程序码,永久有效,5000/min
**/
public static InputStream getWxCodeImageLimit(PublicDTO<GetWxCodeImageLimitDTO> publicDTO){
try {
String str = objectMapper.writeValueAsString(publicDTO.getData());
log.debug(str);
InputStream stream = HttpRequest
.post(String.format(ScanCodeImageEnum.SCAN_CODE_IMAGE.getUrl(),publicDTO.getToken()))
// 超时
.body(str)
.execute().bodyStream();
return stream;
}catch(Exception e){
e.printStackTrace();
}
return null;
}
}
复制代码
二、二维码转黑白
此时,微信开放平台返回给我们一个二维码的文件流,我们将它打印出来看看
InputStream stream = ScanCodeImageSDK.getWxCodeImageLimit(publicDTO);
FileOutputStream fos = new FileOutputStream("D:\download\1.jpg");
IoUtil.write(fos,true,IoUtil.readBytes(stream));
复制代码
虽然彩色的比较好看,但是为了打印方面的需求,需要将它转成黑白的,而微信开放平台默认生成的二维码就是彩色的。
我们需要想办法将其转成黑白。
网上彩色转黑白中写的大多数是文件转文件的形式,而我们的业务中是流转文件,这参考是比较少的。
经过查看方法API,发现可以将流转成图片流,再生成 img对象,调用内部的方法可以做到这点。
具体的实现看下面:
InputStream stream = ScanCodeImageSDK.getWxCodeImageLimit(publicDTO);
ImageInputStream imageInputStream = ImageIO.createImageInputStream(stream);
Img from = Img.from(imageInputStream);
Img gray = from.gray();
Image img = gray.getImg();
ByteArrayInputStream byteArrayInputStream = ImgUtil.toStream(img, "");
复制代码
经过这番操作之后,文件流->转成图片对象->图片对象彩色转黑白->转成文件流的逻辑可以实现彩色转黑白。
三、inputstream 转MultipartFile
我们调用文件服务,需要传入MultipartFile 类型的参数,而不能直接传入流。
这块可以用下面的两个方法进行转换
/**
* 获取封装得MultipartFile
*
* @param inputStream inputStream
* @param fileName fileName
* @return MultipartFile
*/
public MultipartFile getMultipartFile(InputStream inputStream, String fileName) {
FileItem fileItem = createFileItem(inputStream, fileName);
//CommonsMultipartFile是feign对multipartFile的封装,但是要FileItem类对象
return new CommonsMultipartFile(fileItem);
}
/**
* FileItem类对象创建
*
* @param inputStream inputStream
* @param fileName fileName
* @return FileItem
*/
public FileItem createFileItem(InputStream inputStream, String fileName) {
FileItemFactory factory = new DiskFileItemFactory(16, null);
String textFieldName = "file";
FileItem item = factory.createItem(textFieldName, MediaType.MULTIPART_FORM_DATA_VALUE, true, fileName);
int bytesRead = 0;
byte[] buffer = new byte[10 * 1024 * 1024];
OutputStream os = null;
//使用输出流输出输入流的字节
try {
os = item.getOutputStream();
while ((bytesRead = inputStream.read(buffer, 0, 8192)) != -1) {
os.write(buffer, 0, bytesRead);
}
inputStream.close();
} catch (IOException e) {
log.error("Stream copy exception", e);
throw new IllegalArgumentException("文件上传失败");
} finally {
if (os != null) {
try {
os.close();
} catch (IOException e) {
log.error("Stream close exception", e);
}
}
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
log.error("Stream close exception", e);
}
}
}
return item;
}
复制代码
四、文件上传
调用内部文件,采用的是 RPC 通信,通过 Fegin 接口引入。
/**
* @author xiao lei
* @date 2020/8/04 10:02
**/
@FeignClient(value = "giant-server-file",path = "/img")
public interface FileApi {
@PostMapping(value = "admin/push/bucket/path",consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
String pushBack(@RequestPart("file") MultipartFile file,
@RequestParam("bucket") String bucket,
@RequestParam("path") String path,
@RequestHeader("token") String token,
@RequestHeader("key") String key);
}
复制代码
文件服务器是本地部署的 minio 。通过下面代码可以实现文件上传
@Override
public String pushBack(MultipartFile file,String bucket,String path) {
try {
Long milliSecond = LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli();
String id = UUID.randomUUID().toString().replaceAll("-","");
String fileSuffix = ".jpg";
String objName = id + milliSecond + fileSuffix;
path = StrUtil.isBlank(path)?objName:path+"/"+objName;
String str = objectServer.putObject(bucket,path, file.getInputStream());
return fileEnvironmentConfig.getGateway()+bucket+"/"+str;
}catch(Exception e){
e.printStackTrace();
return "";
}
}
复制代码
最后生成的列表如图:
参考文档: 微信开放平台链接