微信小程序后台自动生成小程序码基于阿里云oss实现

本文介绍了如何利用阿里云OSS实现微信小程序自动生成二维码功能,并处理access_token失效问题,通过在访问时检查并刷新access_token保证服务的连续性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

微信小程序自动生成小程序码基于阿里云oss实现并解决多平台access_token失效问题

1、controller

/**
     * @param path       必需参数,指定小程序码的路径
     * @param width      小程序码的宽度,默认为430
     * @param envVersion 可选参数,指定环境版本
     * @description: 获取小程序码。该方法调用quesActivityService的getQRCode方法来生成小程序码,根据提供的路径(path)、宽度(width)和环境版本(envVersion)。
     * @author: lizhiwei
     * @date: 2023/12/15 16:10
     * @return: 小程序码图片的Base64编码字符串
     */
    @GetMapping("/getQRCode")
    public BaseResponse<QRCodeVo> getQRCode(
            @RequestParam(required = true) String path,
            @RequestParam(defaultValue = "430") String width,
            @RequestParam(required = false) String envVersion
    ) {
        QRCodeVo qrCode = quesActivityService.getQRCode(path, width, envVersion);
        return ResultUtils.success(qrCode);
    }

2、业务层service

QRCodeVo getQRCode(String path, String width, String envVersion);
/**
     * @param path       小程序页面路径,必传参数
     * @param width      二维码宽度,有默认值
     * @param envVersion 环境版本参数,非必传
     * @description:获取微信小程序二维码并上传到阿里云OSS。
     * @author: lizhiwei
     * @date: 2023/12/18 10:08
     * @return: 返回阿里云OSS中二维码的URL
     **/
    @Override
    public QRCodeVo getQRCode(String path, String width, String envVersion) {
        // 参数校验
        if (StringUtils.isBlank(path) || StringUtils.isBlank(width)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "路径或宽度参数不能为空");
        }
				//我这里是获取小程序的token(替换成自己的)
        String accessToken = getToken.getMiniProgramAccessToken();
				//我这里是获取小程序码的url(可以写死:https://api.weixin.qq.com/wxa/getwxacode)
        String qrCodeUrl = wxConfig.getQRCodeUrl();
        String url = qrCodeUrl + "?access_token=" + accessToken;

        JSONObject params = new JSONObject();
        params.put("path", path);
        params.put("width", width);
        if (StringUtils.isNotBlank(envVersion)) {
            params.put("env_version", envVersion);
        }

        byte[] responseBytes;
        try {
            responseBytes = doPostAndHandleToken(url, params.toJSONString());
            // 将二进制数据转换为Base64字符串
            String base64Image = Base64.getEncoder().encodeToString(responseBytes);

            try (InputStream inputStream = convertBase64ToInputStream(base64Image)) {
                String ossUrl = uploadToAliyunOSS(inputStream);
                if (StringUtils.isBlank(ossUrl)) {
                    throw new BusinessException(ErrorCode.SYSTEM_ERROR, "图片上传错误,请联系管理员");
                }
                return new QRCodeVo(ossUrl);
            }
        } catch (Exception e) {
            log.error("记录日志:{}", e.getMessage());
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "获取小程序码过程中发生错误");
        }
    }


//这里是检查多平台是否失效的问题,最后我解释一下怎么回事
private byte[] doPostAndHandleToken(String url, String jsonParams) throws IOException {
        byte[] responseBytes = HttpClient.doPostJsonForBytes(url, jsonParams, null);
        String responseBody = new String(responseBytes, StandardCharsets.UTF_8);
        if (responseBody.startsWith("{") && responseBody.endsWith("}")) {
            JSONObject jsonObject = JSONObject.parseObject(responseBody);
//             检查是否有错误码
            if (jsonObject.containsKey("errcode")) {
                int errcode = jsonObject.getIntValue("errcode");
                // 根据具体的错误码处理
                if (errcode == 40001 || errcode == 42001) {
                    // 重新获取access_token(这是我自己的方法,你可以换成自己的获取小程序的token方法,相信你有,因为这个不能老是获取,需要服务统一获取调配)
                    String newAccessToken = getToken.refreshMiniProgramAccessToken();
                    // 重新构建请求URL
                    String newUrl = wxConfig.getQRCodeUrl() + "?access_token=" + newAccessToken;
                    // 重发请求(网络请求的方法没有可以看看我的其他的帖子,有写)
                    responseBytes = HttpClient.doPostJsonForBytes(newUrl, jsonParams, null);
                } else {
                    // 其他错误处理
                    throw new BusinessException(ErrorCode.SYSTEM_ERROR, "微信API错误:" + jsonObject.getString("errmsg"));
                }
            }
        }
        return responseBytes;
    }


/**
     * @param base64Str Base64编码的字符串
     * @description:将Base64编码的字符串转换为InputStream。
     * @author: lizhiwei
     * @date: 2023/12/18 10:30
     * @return: java.io.InputStream 转换后的InputStream
     **/
    public InputStream convertBase64ToInputStream(String base64Str) {
        // 1. 参数校验:确保Base64编码不为空
        if (StringUtils.isBlank(base64Str)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "Base64编码为空");
        }

        // 2. Base64解码并转换为InputStream
        byte[] bytes = Base64.getDecoder().decode(base64Str);
        return new ByteArrayInputStream(bytes);
    }

/**
     * @param inputStream 文件输入流
     * @description:上传文件到阿里云OSS。
     * @author: lizhiwei
     * @date: 2023/12/18 10:29
     * @return: java.lang.String 返回文件在OSS上的URL
     **/
    private String uploadToAliyunOSS(InputStream inputStream) throws Exception {
        //  参数校验:确保输入流不为空
        if (inputStream == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "文件输入流不能为空");
        }
        // 1. 获取OSS配置参数
        String accessKeyId = aliyunOssConfig.getAccessKeyId();
        String accessKeySecret = aliyunOssConfig.getAccessKeySecret();
        String bucketName = aliyunOssConfig.getBucketName();
        String endPoint = aliyunOssConfig.getEndPoint();

        // 2. 调用OSS上传工具上传文件
        return ossUtils.fileUploadThroughBytes(inputStream, endPoint, bucketName, accessKeyId, accessKeySecret);
    }

3、oss工具类方法

 /**
     * @param inputStream     文件的输入流
     * @param endpoint        OSS服务的EndPoint
     * @param bucketName      OSS的存储桶名称
     * @param AccessKeyId     阿里云账号的Access Key ID
     * @param AccessKeySecret 阿里云账号的Access Key Secret
     * @description: 通过字节流上传文件到OSS。该方法接收文件流以及OSS相关的配置信息,
     * 并进行文件上传。在上传之前,会检查必要的参数是否有效。
     * @author: lizhiwei
     * @date: 2023/12/15 14:00
     * @return: 返回图片路径
     */
    public String fileUploadThroughBytes(InputStream inputStream, String endpoint, String bucketName, String AccessKeyId, String AccessKeySecret) {
        // 参数校验
        if (inputStream == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "文件输入流不能为空");
        }
        if (StringUtils.isBlank(endpoint)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "EndPoint不能为空");
        }
        if (StringUtils.isBlank(bucketName)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "Bucket名称不能为空");
        }
        if (StringUtils.isBlank(AccessKeyId) || StringUtils.isBlank(AccessKeySecret)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "阿里云账号、密钥为空");
        }
        Date now = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMM");
        String folderName = sdf.format(now);
        // 获取当前时间戳作为文件名(小程序码固定式jpg 这里就固定下来了)
        String objectName = folderName + "/" + System.currentTimeMillis() + ".jpg";

        // 创建OSSClient实例
        OSS ossClient = null;
        try {
            ossClient = new OSSClientBuilder().build(endpoint, AccessKeyId, AccessKeySecret);
            PutObjectResult result = ossClient.putObject(bucketName, objectName, inputStream);
            if (result.getResponse() != null) {
                log.error("上传失败:" + result.getResponse().getErrorResponseAsString());
                throw new BusinessException(ErrorCode.SYSTEM_ERROR, "阿里云图片上传失败");
            }
            //返回成功上传图片的路径
            return "https://" + bucketName + "." + URI.create(endpoint).getHost() + "/" + objectName;
        } catch (Exception e) {
            log.error("上传文件时发生错误:" + e.getMessage());
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "上传文件时发生错误");
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
    }

4、总结思考

1、背景

为什么要解决多平台获取access_token? 因为如果线上正在使用合理期限的token,你此时接到开发任务,如果你没有多余的小程序作为测试开发使用的,你本地获取一下,那么线上的还在合理使用期的access_token 5分钟后就失效了,但是线上我们一般都采用redis 了,所以会引发问题,带一下官方的帖子,可以自行看一下。

https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/mp-access-token/getAccessToken.html

2、解决办法

因为我们没有一个专门测试的小程序,只有两个线上的,都在使用了,所以我们现在采用的办法就是:在每一个使用该方法的地方都多次验证一下,如果过期了重新获取一下新的access_token,因为本地获取也不是很频繁,所以采用这种方法来完成多环境都需要这个access_token的问题,如果你有更好的方法,欢迎来评论区讨论。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值