管理系统必备技(7): 微信二维码生成及彩色转黑白

管理系统必备技(7): 微信二维码生成及彩色转黑白

这是我参与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));
复制代码

image-20210805175250677

虽然彩色的比较好看,但是为了打印方面的需求,需要将它转成黑白的,而微信开放平台默认生成的二维码就是彩色的。

我们需要想办法将其转成黑白。

网上彩色转黑白中写的大多数是文件转文件的形式,而我们的业务中是流转文件,这参考是比较少的。

经过查看方法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, "");
复制代码

经过这番操作之后,文件流->转成图片对象->图片对象彩色转黑白->转成文件流的逻辑可以实现彩色转黑白。

image-20210805180031184

三、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 "";
        }
    }
复制代码

最后生成的列表如图: image-20210805181507185

参考文档: 微信开放平台链接

分类:
后端