杰瑞科技汇

微信Java如何上传多媒体文件?

  1. 获取 access_token:调用微信任何接口都需要这个凭证。
  2. 调用文件上传接口:使用 access_token 和文件本身,向微信服务器提交文件。

第一步:准备工作

在开始编码之前,请确保你已经完成了以下准备工作:

微信Java如何上传多媒体文件?-图1
(图片来源网络,侵删)
  1. 获取 AppID 和 AppSecret

    • 登录 微信公众平台
    • 进入“开发” -> “基本配置”,找到你的 AppID(应用ID)和 AppSecret(应用密钥)。
    • 注意:如果你是服务号或已认证的订阅号,才有权限调用上传多媒体文件的接口。
  2. 配置服务器 IP 白名单

    • 在“基本配置”页面,找到“开发者身份” -> “服务器配置”。
    • 将你的 Java 后端服务器的公网 IP 地址添加到 IP 白名单中,否则,微信服务器无法回调你的接口,你也无法成功调用微信的接口。

第二步:核心代码实现

我们将使用流行的 Java HTTP 客户端库 OkHttp 来完成文件上传,如果你使用的是 Spring Boot,RestTemplate 也是一个不错的选择。

添加 Maven 依赖

在你的 pom.xml 文件中添加 OkHttp 的依赖:

微信Java如何上传多媒体文件?-图2
(图片来源网络,侵删)
<dependency>
    <groupId>com.squareup.okhttp3</groupId>
    <artifactId>okhttp</artifactId>
    <version>4.9.3</version> <!-- 建议使用较新版本 -->
</dependency>

定义微信 API 常量和工具类

为了代码清晰,我们定义一些常量和一个用于获取 access_token 的工具类。

WeChatApiConstants.java

public final class WeChatApiConstants {
    // 替换成你的 AppID
    public static final String APP_ID = "你的AppID";
    // 替换成你的 AppSecret
    public static final String APP_SECRET = "你的AppSecret";
    // 获取 access_token 的接口
    public static final String ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s";
    // 上传多媒体文件的接口
    public static final String MEDIA_UPLOAD_URL = "https://api.weixin.qq.com/cgi-bin/media/upload?access_token=%s&type=%s";
}

AccessTokenUtil.java 这是一个工具类,用于从微信服务器获取 access_tokenaccess_token 有效期为 2 小时,建议你将其缓存起来(例如使用 Redis),并在过期后重新获取,而不是每次都请求微信。

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import java.io.IOException;
public class AccessTokenUtil {
    private static final OkHttpClient client = new OkHttpClient();
    private static final ObjectMapper mapper = new ObjectMapper();
    // 简单的内存缓存,生产环境请使用 Redis 等专业缓存
    private static String cachedToken;
    private static long tokenExpireTime = 0;
    public static String getAccessToken() throws IOException {
        // token 存在且未过期,直接返回
        if (cachedToken != null && System.currentTimeMillis() < tokenExpireTime) {
            return cachedToken;
        }
        // 否则,向微信服务器请求新的 token
        String url = String.format(WeChatApiConstants.ACCESS_TOKEN_URL, WeChatApiConstants.APP_ID, WeChatApiConstants.APP_SECRET);
        Request request = new Request.Builder().url(url).build();
        try (Response response = client.newCall(request).execute()) {
            if (!response.isSuccessful()) {
                throw new IOException("Unexpected code " + response);
            }
            String responseBody = response.body().string();
            JsonNode jsonNode = mapper.readTree(responseBody);
            if (jsonNode.has("access_token")) {
                // 微信返回的 access_token 有效期为 7200 秒(2小时),我们提前 5 分钟过期
                long expiresIn = jsonNode.get("expires_in").asLong() - 300;
                cachedToken = jsonNode.get("access_token").asText();
                tokenExpireTime = System.currentTimeMillis() + expiresIn * 1000;
                return cachedToken;
            } else {
                // 处理错误情况,例如获取 token 失败
                String errcode = jsonNode.get("errcode").asText();
                String errmsg = jsonNode.get("errmsg").asText();
                throw new RuntimeException("获取微信 access_token 失败: " + errmsg + " (错误码: " + errcode + ")");
            }
        }
    }
}

实现文件上传核心逻辑

我们来实现最关键的上传文件方法。

微信Java如何上传多媒体文件?-图3
(图片来源网络,侵删)
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import okhttp3.*;
import java.io.File;
import java.io.IOException;
public class WeChatMediaUploader {
    private static final OkHttpClient client = new OkHttpClient();
    private static final ObjectMapper mapper = new ObjectMapper();
    private static final MediaType MEDIA_TYPE_PNG = MediaType.parse("image/png");
    /**
     * 上传文件到微信服务器
     *
     * @param filePath 本地文件路径
     * @param mediaType 文件类型,可选: "image", "voice", "video", "thumb"
     * @return 微信服务器返回的媒体ID (media_id)
     * @throws IOException
     */
    public static String uploadMedia(String filePath, String mediaType) throws IOException {
        // 1. 获取 access_token
        String accessToken = AccessTokenUtil.getAccessToken();
        // 2. 构建上传 URL
        String uploadUrl = String.format(WeChatApiConstants.MEDIA_UPLOAD_URL, accessToken, mediaType);
        // 3. 创建文件请求体
        File file = new File(filePath);
        RequestBody fileBody = RequestBody.create(file, MEDIA_TYPE_PNG); // 注意:这里可以根据文件类型动态设置 MediaType
        // 4. 构建请求
        Request request = new Request.Builder()
                .url(uploadUrl)
                .post(fileBody)
                .build();
        // 5. 发送请求并处理响应
        try (Response response = client.newCall(request).execute()) {
            if (!response.isSuccessful()) {
                throw new IOException("Unexpected code " + response + " - " + response.body().string());
            }
            String responseBody = response.body().string();
            System.out.println("微信服务器返回原始数据: " + responseBody);
            JsonNode jsonNode = mapper.readTree(responseBody);
            // 6. 解析返回结果
            if (jsonNode.has("media_id")) {
                return jsonNode.get("media_id").asText();
            } else {
                // 处理错误情况
                String errcode = jsonNode.get("errcode").asText();
                String errmsg = jsonNode.get("errmsg").asText();
                throw new RuntimeException("上传文件到微信失败: " + errmsg + " (错误码: " + errcode + ")");
            }
        }
    }
    public static void main(String[] args) {
        try {
            // 示例:上传一张图片
            // 请确保 "path/to/your/image.png" 这个路径是正确的
            String mediaId = uploadMedia("path/to/your/image.png", "image");
            System.out.println("上传成功!媒体ID为: " + mediaId);
            // 你可以使用这个 media_id 进行后续操作,
            // 1. 发送客服消息
            // 2. 生成永久素材
            // 3. 上传图文消息内的图片
        } catch (IOException e) {
            e.printStackTrace();
        } catch (RuntimeException e) {
            System.err.println(e.getMessage());
        }
    }
}

第三步:代码解读与注意事项

  1. access_token 缓存

    • AccessTokenUtil 中做了一个简单的内存缓存。在生产环境中,强烈建议你使用 Redis 等分布式缓存,因为你的应用可能是多实例部署,内存缓存会导致不同实例的 access_token 不同步,并且应用重启后会失效。
  2. 文件类型 (type)

    • type 参数必须是以下四种之一:image, voice, video, thumb
    • image: 图片(JPG, PNG, GIF)
    • voice: 语音(AMR, AAC)
    • video: 视频(MP4)
    • thumb: 缩略图(用于视频消息的封面,建议尺寸为 160x160 的 JPG)
  3. 文件大小限制

    • 图片:1MB
    • 语音:2MB,播放长度不超过 60 秒
    • 视频:10MB
    • 缩略图:64KB
    • 超过大小限制的请求会失败。
  4. MediaType

    • uploadMedia 方法中,我使用了 MediaType.parse("image/png"),这是一个硬编码的示例。
    • 在实际应用中,你应该根据上传文件的真实 MIME 类型来动态设置 MediaType,对于 .jpg 文件,应使用 image/jpeg,你可以使用 Files.probeContentType() 来探测文件类型。
  5. 错误处理

    • 微信接口在失败时会返回 JSON 格式的错误信息,包含 errcodeerrmsg,代码中已经包含了基本的错误解析和抛出逻辑,你需要捕获这些异常并做相应处理(例如记录日志、返回错误信息给前端等)。
  6. 返回结果

    • 上传成功后,微信服务器会返回一个 JSON 对象,其中包含 media_idcreated_at 等字段。media_id 是后续操作(如发送消息)的关键,请务必妥善保存。

第四步:进阶与常见问题

如何上传临时素材 vs 永久素材?

  • 临时素材:我们上面实现的 cgi-bin/media/upload 接口上传的就是临时素材,它会在 3 天后自动被微信服务器删除,适用于临时性的消息发送。
  • 永久素材:如果你想长期保存这些文件(例如公众号文章的图片、视频),应该使用 cgi-bin/material/add_material 接口,这个接口的调用方式与临时素材类似,但 URL 不同,并且可以上传更复杂的图文消息,返回的结果中会有一个 media_id,这个 media_id 是永久的(除非你手动删除)。

如何在 Spring Boot 中实现?

如果你使用的是 Spring Boot,代码结构会更清晰。

  1. 创建一个 Service

    @Service
    public class WeChatService {
        @Value("${wechat.app-id}")
        private String appId;
        @Value("${wechat.app-secret}")
        private String appSecret;
        @Autowired
        private RedisTemplate<String, String> redisTemplate;
        public String getAccessToken() {
            String tokenKey = "wechat:access_token:" + appId;
            String token = redisTemplate.opsForValue().get(tokenKey);
            if (StringUtils.hasText(token)) {
                return token;
            }
            // ... 使用 RestTemplate 或 OkHttp 调用微信接口获取新 token ...
            // ... 获取后存入 Redis,并设置过期时间 ...
            //  redisTemplate.opsForValue().set(tokenKey, newToken, 7000, TimeUnit.SECONDS);
        }
        public String uploadMedia(MultipartFile file, String mediaType) {
            String accessToken = getAccessToken();
            // ... 使用 RestTemplate 的 MultiValueMap 来构建文件上传请求 ...
            // ...
        }
    }
  2. 使用 RestTemplate 上传文件

    // 在 Controller 中接收前端传来的文件
    @PostMapping("/upload")
    public ResponseEntity<String> upload(@RequestParam("file") MultipartFile file) {
        try {
            String mediaId = weChatService.uploadMedia(file, "image");
            return ResponseEntity.ok("上传成功,media_id: " + mediaId);
        } catch (Exception e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("上传失败: " + e.getMessage());
        }
    }

希望这份详细的指南能帮助你成功地在 Java 后端实现微信多媒体文件的上传功能!

分享:
扫描分享到社交APP
上一篇
下一篇