🆕 #3320【企业微信】增加异步上传临时素材相关接口

This commit is contained in:
imyzt 2025-04-29 11:29:09 +08:00 committed by GitHub
parent 2279105fef
commit 2d8d1df00e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 261 additions and 1 deletions

View File

@ -2,6 +2,8 @@ package me.chanjar.weixin.cp.api;
import me.chanjar.weixin.common.bean.result.WxMediaUploadResult;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.cp.bean.media.MediaUploadByUrlReq;
import me.chanjar.weixin.cp.bean.media.MediaUploadByUrlResult;
import java.io.File;
import java.io.IOException;
@ -133,4 +135,21 @@ public interface WxCpMediaService {
* @throws WxErrorException the wx error exception
*/
String uploadImg(File file) throws WxErrorException;
/**
* 生成异步上传任务
* 跟上传临时素材拿到的media_id使用场景是不通用的目前适配的接口如下https://developer.work.weixin.qq.com/document/path/96488#%E4%BD%BF%E7%94%A8%E5%9C%BA%E6%99%AF%E8%AF%B4%E6%98%8E
* @param req 请求参数
* @return 返回异步任务id
* @throws WxErrorException the wx error exception
*/
String uploadByUrl(MediaUploadByUrlReq req) throws WxErrorException;
/**
* 查询异步任务结果
* @param jobId 任务id最长为128字节60分钟内有效
* @return 返回异步任务结果
* @throws WxErrorException the wx error exception
*/
MediaUploadByUrlResult uploadByUrl(String jobId) throws WxErrorException;
}

View File

@ -1,5 +1,6 @@
package me.chanjar.weixin.cp.api.impl;
import com.google.gson.JsonObject;
import lombok.RequiredArgsConstructor;
import me.chanjar.weixin.common.bean.result.WxMediaUploadResult;
import me.chanjar.weixin.common.error.WxErrorException;
@ -9,8 +10,12 @@ import me.chanjar.weixin.common.util.http.BaseMediaDownloadRequestExecutor;
import me.chanjar.weixin.common.util.http.InputStreamData;
import me.chanjar.weixin.common.util.http.MediaInputStreamUploadRequestExecutor;
import me.chanjar.weixin.common.util.http.MediaUploadRequestExecutor;
import me.chanjar.weixin.common.util.json.GsonHelper;
import me.chanjar.weixin.common.util.json.GsonParser;
import me.chanjar.weixin.cp.api.WxCpMediaService;
import me.chanjar.weixin.cp.api.WxCpService;
import me.chanjar.weixin.cp.bean.media.MediaUploadByUrlReq;
import me.chanjar.weixin.cp.bean.media.MediaUploadByUrlResult;
import java.io.File;
import java.io.IOException;
@ -20,7 +25,12 @@ import java.net.URL;
import java.nio.file.Files;
import java.util.UUID;
import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.Media.*;
import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.Media.GET_UPLOAD_BY_URL_RESULT;
import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.Media.IMG_UPLOAD;
import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.Media.JSSDK_MEDIA_GET;
import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.Media.MEDIA_GET;
import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.Media.MEDIA_UPLOAD;
import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.Media.UPLOAD_BY_URL;
/**
* <pre>
@ -119,4 +129,20 @@ public class WxCpMediaServiceImpl implements WxCpMediaService {
return this.mainService.execute(MediaUploadRequestExecutor.create(this.mainService.getRequestHttp()), url, file)
.getUrl();
}
@Override
public String uploadByUrl(MediaUploadByUrlReq req) throws WxErrorException {
final String url = this.mainService.getWxCpConfigStorage().getApiUrl(UPLOAD_BY_URL);
String responseContent = this.mainService.post(url, req.toJson());
return GsonHelper.getString(GsonParser.parse(responseContent), "jobid");
}
@Override
public MediaUploadByUrlResult uploadByUrl(String jobId) throws WxErrorException {
final String url = this.mainService.getWxCpConfigStorage().getApiUrl(GET_UPLOAD_BY_URL_RESULT);
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("jobid", jobId);
String post = this.mainService.post(url, jsonObject.toString());
return MediaUploadByUrlResult.fromJson(post);
}
}

View File

@ -0,0 +1,58 @@
package me.chanjar.weixin.cp.bean.media;
import lombok.Data;
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
/**
* 生成异步上传任务
* @author <a href="https://github.com/imyzt">imyzt</a>
* @date 2025/04/27
*/
@Data
public class MediaUploadByUrlReq {
/**
* 场景值1-客户联系入群欢迎语素材目前仅支持1 注意每个场景值有对应的使用范围详见上面的使用场景说明
*/
private Integer scene;
/**
* 媒体文件类型目前仅支持video-视频file-普通文件 不超过32字节
*/
private String type;
/**
* 文件名标识文件展示的名称比如使用该media_id发消息时展示的文件名由该字段控制 不超过128字节
*/
private String filename;
/**
* 文件cdn urlurl要求支持Range分块下载 不超过1024字节 如果为腾讯云cos链接则需要设置为公有读权限
*/
private String url;
/**
* 文件md5对比从url下载下来的文件md5是否一致 不超过32字节
*/
private String md5;
/**
* From json wx cp base resp.
*
* @param json the json
* @return the wx cp base resp
*/
public static MediaUploadByUrlReq fromJson(String json) {
return WxCpGsonBuilder.create().fromJson(json, MediaUploadByUrlReq.class);
}
/**
* To json string.
*
* @return the string
*/
public String toJson() {
return WxCpGsonBuilder.create().toJson(this);
}
}

View File

@ -0,0 +1,82 @@
package me.chanjar.weixin.cp.bean.media;
import com.google.gson.annotations.SerializedName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import me.chanjar.weixin.cp.bean.WxCpBaseResp;
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
import java.io.Serializable;
/**
* 异步上传企微素材
* @author <a href="https://github.com/imyzt">imyzt</a>
* @date 2025/4/27
*/
@EqualsAndHashCode(callSuper = true)
@Data
public class MediaUploadByUrlResult extends WxCpBaseResp implements Serializable {
private static final long serialVersionUID = 330834334738622341L;
/**
* 任务状态1-处理中2-完成3-异常失败
*/
@SerializedName("status")
private Integer status;
@SerializedName("detail")
private Detail detail;
@Data
public static class Detail {
/**
* 任务失败返回码当status为3时返回非0其他返回0
* 830001 url非法 确认url是否支持Range分块下载
* 830003 url下载数据失败 确认url本身是否能正常访问
* 45001 文件大小超过限制 确认文件在5字节~200M范围内
* 301019 文件MD5不匹配 确认url对应的文件内容md5跟所填的md5参数是否一致
* 注意: status=2时,此处微信并未返回任何值
*/
@SerializedName("errcode")
private Integer errCode;
/**
* 注意: status=2时,此处微信并未返回任何值
*/
@SerializedName("errmsg")
private String errMsg;
/**
* 媒体文件上传后获取的唯一标识3天内有效当status为2时返回
*/
@SerializedName("media_id")
private String mediaId;
/**
* 媒体文件创建的时间戳当status为2时返回
*/
@SerializedName("created_at")
private String createdAt;
}
/**
* From json wx cp media upload by url result.
*
* @param json the json
* @return the wx cp media upload by url result
*/
public static MediaUploadByUrlResult fromJson(String json) {
return WxCpGsonBuilder.create().fromJson(json, MediaUploadByUrlResult.class);
}
/**
* To json string.
*
* @return the string
*/
public String toJson() {
return WxCpGsonBuilder.create().toJson(this);
}
}

View File

@ -198,6 +198,13 @@ public class WxCpXmlMessage implements Serializable {
@XStreamAlias("SelectedItems")
private List<SelectedItem> selectedItems;
/**
* <a href="https://developer.work.weixin.qq.com/document/path/96488#%E5%9B%9E%E8%B0%83%E5%BC%82%E6%AD%A5%E4%BB%BB%E5%8A%A1%E7%BB%93%E6%9E%9C">异步任务id</a>
*/
@XStreamAlias("JobId")
@XStreamConverter(value = XStreamCDataConverter.class)
private String jobId;
/**
* 微信客服
* 调用拉取消息接口时需要传此token用于校验请求的合法性

View File

@ -238,6 +238,12 @@ public interface WxCpApiPathConsts {
* The constant JSSDK_MEDIA_GET.
*/
String JSSDK_MEDIA_GET = "/cgi-bin/media/get/jssdk";
/** The constant GET_UPLOAD_BY_URL_RESULT. */
String GET_UPLOAD_BY_URL_RESULT = "/cgi-bin/media/get_upload_by_url_result";
/** The constant UPLOAD_BY_URL. */
String UPLOAD_BY_URL = "/cgi-bin/media/upload_by_url";
}
/**

View File

@ -219,6 +219,11 @@ public class WxCpConsts {
*/
public static final String CUSTOMER_ACQUISITION = "customer_acquisition";
/**
* <a href="https://developer.work.weixin.qq.com/document/path/96488#%E5%9B%9E%E8%B0%83%E5%BC%82%E6%AD%A5%E4%BB%BB%E5%8A%A1%E7%BB%93%E6%9E%9C">异步上传临时素材结果回调通知</a>
*/
public static final String UPLOAD_MEDIA_JOB_FINISH = "upload_media_job_finish";
}
/**

View File

@ -7,6 +7,8 @@ import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.cp.api.ApiTestModule;
import me.chanjar.weixin.cp.api.TestConstants;
import me.chanjar.weixin.cp.api.WxCpService;
import me.chanjar.weixin.cp.bean.media.MediaUploadByUrlReq;
import me.chanjar.weixin.cp.bean.media.MediaUploadByUrlResult;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Guice;
import org.testng.annotations.Test;
@ -127,4 +129,38 @@ public class WxCpMediaServiceImplTest {
assertThat(file).isNotNull();
System.out.println(file);
}
/**
* Test upload media by url.
*
* @throws WxErrorException the wx error exception
*/
@Test
public void testUploadMediaByUrl() throws WxErrorException {
MediaUploadByUrlReq req = new MediaUploadByUrlReq();
req.setScene(1);
req.setType("video");
req.setFilename("mov_bbb");
req.setUrl("https://www.w3school.com.cn/example/html5/mov_bbb.mp4");
req.setMd5("198918f40ecc7cab0fc4231adaf67c96");
String jobId = this.wxService.getMediaService().uploadByUrl(req);
System.out.println(jobId);
}
/**
* Test upload media by url.
*
* @throws WxErrorException the wx error exception
*/
@Test
public void testUploadMediaByUrlResult() throws WxErrorException, InterruptedException {
String jobId = "job1745801375_5GIKWuFF3M7hcIkeSNMqs_W26xy5VeSWjLaLFTEdSfQ";
MediaUploadByUrlResult result = this.wxService.getMediaService().uploadByUrl(jobId);
System.out.println(result);
}
@Test
public void testUploadMediaJobFinishEvent() throws WxErrorException {
File file = this.wxService.getMediaService().getJssdkFile("....");
}
}

View File

@ -6,6 +6,7 @@ import me.chanjar.weixin.cp.util.xml.XStreamTransformer;
import org.testng.annotations.Test;
import static me.chanjar.weixin.cp.constant.WxCpConsts.EventType.TASKCARD_CLICK;
import static me.chanjar.weixin.cp.constant.WxCpConsts.EventType.UPLOAD_MEDIA_JOB_FINISH;
import static org.assertj.core.api.Assertions.assertThat;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
@ -421,4 +422,24 @@ public class WxCpXmlMessageTest {
assertThat(wxCpXmlMessage.getApprovalInfo().getApprovalNodes().get(0).getItems().get(0).getItemName()).isNotEmpty();
assertThat(wxCpXmlMessage.getApprovalInfo().getNotifyNodes().get(0).getItemName()).isNotEmpty();
}
/**
* Test open approval change.
*/
public void testUploadMediaJobFinishEvent() {
String xml = "<xml>\n" +
"\t<ToUserName><![CDATA[wx28dbb14e3720FAKE]]></ToUserName>\n" +
"\t<FromUserName><![CDATA[sys]]></FromUserName>\n" +
"\t<CreateTime>1425284517</CreateTime>\n" +
"\t<MsgType><![CDATA[event]]></MsgType>\n" +
"\t<Event><![CDATA[upload_media_job_finish]]></Event>\n" +
"\t<JobId><![CDATA[jobid_S0MrnndvRG5fadSlLwiBqiDDbM143UqTmKP3152FZk4]]></JobId>\n" +
"</xml>";
WxCpXmlMessage wxCpXmlMessage = WxCpXmlMessage.fromXml(xml);
assertThat(wxCpXmlMessage).isNotNull();
assertThat(wxCpXmlMessage.getJobId()).isNotEmpty();
assertThat(wxCpXmlMessage.getJobId()).isEqualTo("jobid_S0MrnndvRG5fadSlLwiBqiDDbM143UqTmKP3152FZk4");
assertThat(wxCpXmlMessage.getEvent()).isEqualTo(UPLOAD_MEDIA_JOB_FINISH);
}
}