diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/CommonUploadParam.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/CommonUploadParam.java index 3a9872fc9..afa7000e8 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/CommonUploadParam.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/CommonUploadParam.java @@ -10,6 +10,8 @@ import org.springframework.lang.Nullable; import java.io.ByteArrayInputStream; import java.io.File; import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; /** * 通用文件上传参数 @@ -34,6 +36,13 @@ public class CommonUploadParam implements Serializable { @NotNull private CommonUploadData data; + /** + * 额外的表单字段,用于在上传文件的同时提交其他表单数据 + * 例如:上传视频素材时需要提交description字段(JSON格式的视频描述信息) + */ + @Nullable + private Map formFields; + /** * 从文件构造 * @@ -43,7 +52,7 @@ public class CommonUploadParam implements Serializable { */ @SneakyThrows public static CommonUploadParam fromFile(String name, File file) { - return new CommonUploadParam(name, CommonUploadData.fromFile(file)); + return new CommonUploadParam(name, CommonUploadData.fromFile(file), null); } /** @@ -55,11 +64,26 @@ public class CommonUploadParam implements Serializable { */ @SneakyThrows public static CommonUploadParam fromBytes(String name, @Nullable String fileName, byte[] bytes) { - return new CommonUploadParam(name, new CommonUploadData(fileName, new ByteArrayInputStream(bytes), bytes.length)); + return new CommonUploadParam(name, new CommonUploadData(fileName, new ByteArrayInputStream(bytes), bytes.length), null); + } + + /** + * 添加额外的表单字段 + * + * @param fieldName 表单字段名 + * @param fieldValue 表单字段值 + * @return 当前对象,支持链式调用 + */ + public CommonUploadParam addFormField(String fieldName, String fieldValue) { + if (this.formFields == null) { + this.formFields = new HashMap<>(); + } + this.formFields.put(fieldName, fieldValue); + return this; } @Override public String toString() { - return String.format("{name:%s, data:%s}", name, data); + return String.format("{name:%s, data:%s, formFields:%s}", name, data, formFields); } } diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/executor/CommonUploadRequestExecutorApacheImpl.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/executor/CommonUploadRequestExecutorApacheImpl.java index 7f19241cd..dba92e27d 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/executor/CommonUploadRequestExecutorApacheImpl.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/executor/CommonUploadRequestExecutorApacheImpl.java @@ -44,11 +44,19 @@ public class CommonUploadRequestExecutorApacheImpl extends CommonUploadRequestEx if (param != null) { CommonUploadData data = param.getData(); InnerStreamBody part = new InnerStreamBody(data.getInputStream(), ContentType.DEFAULT_BINARY, data.getFileName(), data.getLength()); - HttpEntity entity = MultipartEntityBuilder + MultipartEntityBuilder entityBuilder = MultipartEntityBuilder .create() .addPart(param.getName(), part) - .setMode(HttpMultipartMode.RFC6532) - .build(); + .setMode(HttpMultipartMode.RFC6532); + + // 添加额外的表单字段 + if (param.getFormFields() != null && !param.getFormFields().isEmpty()) { + for (java.util.Map.Entry entry : param.getFormFields().entrySet()) { + entityBuilder.addTextBody(entry.getKey(), entry.getValue(), ContentType.TEXT_PLAIN.withCharset("UTF-8")); + } + } + + HttpEntity entity = entityBuilder.build(); httpPost.setEntity(entity); } String responseContent = requestHttp.getRequestHttpClient().execute(httpPost, Utf8ResponseHandler.INSTANCE); diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/executor/CommonUploadRequestExecutorHttpComponentsImpl.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/executor/CommonUploadRequestExecutorHttpComponentsImpl.java index f79eaa49b..f79e4cd96 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/executor/CommonUploadRequestExecutorHttpComponentsImpl.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/executor/CommonUploadRequestExecutorHttpComponentsImpl.java @@ -41,11 +41,19 @@ public class CommonUploadRequestExecutorHttpComponentsImpl extends CommonUploadR if (param != null) { CommonUploadData data = param.getData(); InnerStreamBody part = new InnerStreamBody(data.getInputStream(), ContentType.DEFAULT_BINARY, data.getFileName(), data.getLength()); - HttpEntity entity = MultipartEntityBuilder + MultipartEntityBuilder entityBuilder = MultipartEntityBuilder .create() .addPart(param.getName(), part) - .setMode(HttpMultipartMode.EXTENDED) - .build(); + .setMode(HttpMultipartMode.EXTENDED); + + // 添加额外的表单字段 + if (param.getFormFields() != null && !param.getFormFields().isEmpty()) { + for (java.util.Map.Entry entry : param.getFormFields().entrySet()) { + entityBuilder.addTextBody(entry.getKey(), entry.getValue(), ContentType.TEXT_PLAIN.withCharset("UTF-8")); + } + } + + HttpEntity entity = entityBuilder.build(); httpPost.setEntity(entity); } String responseContent = requestHttp.getRequestHttpClient().execute(httpPost, Utf8ResponseHandler.INSTANCE); diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/executor/CommonUploadRequestExecutorJoddHttpImpl.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/executor/CommonUploadRequestExecutorJoddHttpImpl.java index 36e8660f7..182820d07 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/executor/CommonUploadRequestExecutorJoddHttpImpl.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/executor/CommonUploadRequestExecutorJoddHttpImpl.java @@ -39,6 +39,14 @@ public class CommonUploadRequestExecutorJoddHttpImpl extends CommonUploadRequest } request.withConnectionProvider(requestHttp.getRequestHttpClient()); request.form(param.getName(), new CommonUploadParamToUploadableAdapter(param.getData())); + + // 添加额外的表单字段 + if (param.getFormFields() != null && !param.getFormFields().isEmpty()) { + for (java.util.Map.Entry entry : param.getFormFields().entrySet()) { + request.form(entry.getKey(), entry.getValue()); + } + } + HttpResponse response = request.send(); response.charset(StandardCharsets.UTF_8.name()); String responseContent = response.bodyText(); diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/executor/CommonUploadRequestExecutorOkHttpImpl.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/executor/CommonUploadRequestExecutorOkHttpImpl.java index 40a4622b8..6a0343980 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/executor/CommonUploadRequestExecutorOkHttpImpl.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/executor/CommonUploadRequestExecutorOkHttpImpl.java @@ -31,10 +31,18 @@ public class CommonUploadRequestExecutorOkHttpImpl extends CommonUploadRequestEx @Override public String execute(String uri, CommonUploadParam param, WxType wxType) throws WxErrorException, IOException { RequestBody requestBody = new CommonUpdateDataToRequestBodyAdapter(param.getData()); - RequestBody body = new MultipartBody.Builder() + MultipartBody.Builder bodyBuilder = new MultipartBody.Builder() .setType(MediaType.get("multipart/form-data")) - .addFormDataPart(param.getName(), param.getData().getFileName(), requestBody) - .build(); + .addFormDataPart(param.getName(), param.getData().getFileName(), requestBody); + + // 添加额外的表单字段 + if (param.getFormFields() != null && !param.getFormFields().isEmpty()) { + for (java.util.Map.Entry entry : param.getFormFields().entrySet()) { + bodyBuilder.addFormDataPart(entry.getKey(), entry.getValue()); + } + } + + RequestBody body = bodyBuilder.build(); Request request = new Request.Builder().url(uri).post(body).build(); try (Response response = requestHttp.getRequestHttpClient().newCall(request).execute()) { diff --git a/weixin-java-common/src/test/java/me/chanjar/weixin/common/bean/CommonUploadParamTest.java b/weixin-java-common/src/test/java/me/chanjar/weixin/common/bean/CommonUploadParamTest.java new file mode 100644 index 000000000..8afcdd741 --- /dev/null +++ b/weixin-java-common/src/test/java/me/chanjar/weixin/common/bean/CommonUploadParamTest.java @@ -0,0 +1,98 @@ +package me.chanjar.weixin.common.bean; + +import org.testng.Assert; +import org.testng.annotations.Test; + +import java.io.File; +import java.util.HashMap; +import java.util.Map; + +/** + * CommonUploadParam 单元测试 + * + * @author Binary Wang + */ +@Test +public class CommonUploadParamTest { + + @Test + public void testFromFile() { + File file = new File("test.txt"); + CommonUploadParam param = CommonUploadParam.fromFile("media", file); + + Assert.assertNotNull(param); + Assert.assertEquals(param.getName(), "media"); + Assert.assertNotNull(param.getData()); + Assert.assertNull(param.getFormFields()); + } + + @Test + public void testFromBytes() { + byte[] bytes = "test content".getBytes(); + CommonUploadParam param = CommonUploadParam.fromBytes("media", "test.txt", bytes); + + Assert.assertNotNull(param); + Assert.assertEquals(param.getName(), "media"); + Assert.assertNotNull(param.getData()); + Assert.assertEquals(param.getData().getFileName(), "test.txt"); + Assert.assertNull(param.getFormFields()); + } + + @Test + public void testAddFormField() { + File file = new File("test.txt"); + CommonUploadParam param = CommonUploadParam.fromFile("media", file); + + // 添加单个表单字段 + param.addFormField("title", "测试标题"); + + Assert.assertNotNull(param.getFormFields()); + Assert.assertEquals(param.getFormFields().size(), 1); + Assert.assertEquals(param.getFormFields().get("title"), "测试标题"); + + // 添加多个表单字段 + param.addFormField("introduction", "测试介绍"); + + Assert.assertEquals(param.getFormFields().size(), 2); + Assert.assertEquals(param.getFormFields().get("introduction"), "测试介绍"); + } + + @Test + public void testAddFormFieldChaining() { + File file = new File("test.txt"); + CommonUploadParam param = CommonUploadParam.fromFile("media", file) + .addFormField("title", "测试标题") + .addFormField("introduction", "测试介绍"); + + Assert.assertNotNull(param.getFormFields()); + Assert.assertEquals(param.getFormFields().size(), 2); + Assert.assertEquals(param.getFormFields().get("title"), "测试标题"); + Assert.assertEquals(param.getFormFields().get("introduction"), "测试介绍"); + } + + @Test + public void testConstructorWithFormFields() { + CommonUploadData data = new CommonUploadData("test.txt", null, 0); + Map formFields = new HashMap<>(); + formFields.put("title", "测试标题"); + formFields.put("introduction", "测试介绍"); + + CommonUploadParam param = new CommonUploadParam("media", data, formFields); + + Assert.assertNotNull(param.getFormFields()); + Assert.assertEquals(param.getFormFields().size(), 2); + Assert.assertEquals(param.getFormFields().get("title"), "测试标题"); + Assert.assertEquals(param.getFormFields().get("introduction"), "测试介绍"); + } + + @Test + public void testToString() { + File file = new File("test.txt"); + CommonUploadParam param = CommonUploadParam.fromFile("media", file) + .addFormField("title", "测试标题"); + + String str = param.toString(); + Assert.assertTrue(str.contains("name:media")); + Assert.assertTrue(str.contains("formFields:")); + } +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenMaAuthServiceImpl.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenMaAuthServiceImpl.java index eae12acae..a3ab3e13e 100644 --- a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenMaAuthServiceImpl.java +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenMaAuthServiceImpl.java @@ -38,7 +38,7 @@ public class WxOpenMaAuthServiceImpl implements WxOpenMaAuthService { @Override public MaAuthUploadResult upload(CommonUploadData data) throws WxErrorException { - String response = wxMaService.upload(OPEN_MA_AUTH_UPLOAD, new CommonUploadParam("media", data)); + String response = wxMaService.upload(OPEN_MA_AUTH_UPLOAD, new CommonUploadParam("media", data, null)); return WxMaGsonBuilder.create().fromJson(response, MaAuthUploadResult.class); } diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/icp/WxOpenUploadIcpMediaParam.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/icp/WxOpenUploadIcpMediaParam.java index e431ab870..b106b33c9 100644 --- a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/icp/WxOpenUploadIcpMediaParam.java +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/icp/WxOpenUploadIcpMediaParam.java @@ -57,7 +57,7 @@ public class WxOpenUploadIcpMediaParam implements Serializable { CommonUploadMultiParam.NormalParam.builder().name("certificate_type").value(String.valueOf(certificateType)).build(), CommonUploadMultiParam.NormalParam.builder().name("icp_order_field").value(icpOrderField).build() )) - .uploadParam(new CommonUploadParam("media", media)) + .uploadParam(new CommonUploadParam("media", media, null)) .build(); }