🆕 【开放平台】接入小程序认证(年审)相关接口,同时增加公共的文件上传方法
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
package me.chanjar.weixin.common.bean;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.file.Files;
|
||||
|
||||
/**
|
||||
* 通用文件上传数据
|
||||
*
|
||||
* @author <a href="https://www.sacoc.cn">广州跨界</a>
|
||||
* created on 2024/01/11
|
||||
*/
|
||||
@Slf4j
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class CommonUploadData implements Serializable {
|
||||
|
||||
/**
|
||||
* 文件名,如:1.jpg
|
||||
*/
|
||||
@Nullable
|
||||
private String fileName;
|
||||
|
||||
/**
|
||||
* 文件内容
|
||||
*
|
||||
* @see FileInputStream 文件输入流
|
||||
* @see ByteArrayInputStream 字节输入流
|
||||
*/
|
||||
@NotNull
|
||||
private InputStream inputStream;
|
||||
|
||||
/**
|
||||
* 文件内容长度(字节数)
|
||||
*/
|
||||
private long length;
|
||||
|
||||
/**
|
||||
* 从文件构造
|
||||
*
|
||||
* @param file 文件
|
||||
* @return 通用文件上传数据
|
||||
*/
|
||||
@SneakyThrows
|
||||
public static CommonUploadData fromFile(File file) {
|
||||
return new CommonUploadData(file.getName(), Files.newInputStream(file.toPath()), file.length());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 读取所有字节,此方法会关闭输入流
|
||||
*
|
||||
* @return 字节数组
|
||||
*/
|
||||
@SneakyThrows
|
||||
public byte[] readAllBytes() {
|
||||
byte[] bytes = new byte[(int) length];
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
inputStream.read(bytes);
|
||||
inputStream.close();
|
||||
return bytes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("{fileName:%s, length:%s}", fileName, length);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package me.chanjar.weixin.common.bean;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.SneakyThrows;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 通用文件上传参数
|
||||
*
|
||||
* @author <a href="https://www.sacoc.cn">广州跨界</a>
|
||||
* created on 2024/01/11
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class CommonUploadParam implements Serializable {
|
||||
|
||||
/**
|
||||
* 文件对应的接口参数名称(非文件名),如:media
|
||||
*/
|
||||
@NotNull
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 上传数据
|
||||
*/
|
||||
@NotNull
|
||||
private CommonUploadData data;
|
||||
|
||||
/**
|
||||
* 从文件构造
|
||||
*
|
||||
* @param name 参数名,如:media
|
||||
* @param file 文件
|
||||
* @return 文件上传参数对象
|
||||
*/
|
||||
@SneakyThrows
|
||||
public static CommonUploadParam fromFile(String name, File file) {
|
||||
return new CommonUploadParam(name, CommonUploadData.fromFile(file));
|
||||
}
|
||||
|
||||
/**
|
||||
* 从字节数组构造
|
||||
*
|
||||
* @param name 参数名,如:media
|
||||
* @param bytes 字节数组
|
||||
* @return 文件上传参数对象
|
||||
*/
|
||||
@SneakyThrows
|
||||
public static CommonUploadParam fromBytes(String name, @Nullable String fileName, byte[] bytes) {
|
||||
return new CommonUploadParam(name, new CommonUploadData(fileName, new ByteArrayInputStream(bytes), bytes.length));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("{name:%s, data:%s}", name, data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package me.chanjar.weixin.common.executor;
|
||||
|
||||
import me.chanjar.weixin.common.bean.CommonUploadParam;
|
||||
import me.chanjar.weixin.common.enums.WxType;
|
||||
import me.chanjar.weixin.common.error.WxErrorException;
|
||||
import me.chanjar.weixin.common.util.http.RequestExecutor;
|
||||
import me.chanjar.weixin.common.util.http.RequestHttp;
|
||||
import me.chanjar.weixin.common.util.http.ResponseHandler;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* 通用文件上传执行器
|
||||
*
|
||||
* @author <a href="https://www.sacoc.cn">广州跨界</a>
|
||||
* created on 2024/01/11
|
||||
*/
|
||||
public abstract class CommonUploadRequestExecutor<H, P> implements RequestExecutor<String, CommonUploadParam> {
|
||||
|
||||
protected RequestHttp<H, P> requestHttp;
|
||||
|
||||
public CommonUploadRequestExecutor(RequestHttp<H, P> requestHttp) {
|
||||
this.requestHttp = requestHttp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(String uri, CommonUploadParam data, ResponseHandler<String> handler, WxType wxType) throws WxErrorException, IOException {
|
||||
handler.handle(this.execute(uri, data, wxType));
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造通用文件上传执行器
|
||||
*
|
||||
* @param requestHttp 请求信息
|
||||
* @return 执行器
|
||||
*/
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
public static RequestExecutor<String, CommonUploadParam> create(RequestHttp requestHttp) {
|
||||
switch (requestHttp.getRequestType()) {
|
||||
case APACHE_HTTP:
|
||||
return new CommonUploadRequestExecutorApacheImpl(requestHttp);
|
||||
case JODD_HTTP:
|
||||
return new CommonUploadRequestExecutorJoddHttpImpl(requestHttp);
|
||||
case OK_HTTP:
|
||||
return new CommonUploadRequestExecutorOkHttpImpl(requestHttp);
|
||||
default:
|
||||
throw new IllegalArgumentException("不支持的http执行器类型:" + requestHttp.getRequestType());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package me.chanjar.weixin.common.executor;
|
||||
|
||||
import lombok.Getter;
|
||||
import me.chanjar.weixin.common.bean.CommonUploadData;
|
||||
import me.chanjar.weixin.common.bean.CommonUploadParam;
|
||||
import me.chanjar.weixin.common.enums.WxType;
|
||||
import me.chanjar.weixin.common.error.WxError;
|
||||
import me.chanjar.weixin.common.error.WxErrorException;
|
||||
import me.chanjar.weixin.common.util.http.RequestHttp;
|
||||
import me.chanjar.weixin.common.util.http.apache.Utf8ResponseHandler;
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.HttpHost;
|
||||
import org.apache.http.client.config.RequestConfig;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.entity.ContentType;
|
||||
import org.apache.http.entity.mime.HttpMultipartMode;
|
||||
import org.apache.http.entity.mime.MultipartEntityBuilder;
|
||||
import org.apache.http.entity.mime.content.InputStreamBody;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* Apache HttpClient 通用文件上传器
|
||||
*
|
||||
* @author <a href="https://www.sacoc.cn">广州跨界</a>
|
||||
* created on 2024/01/11
|
||||
*/
|
||||
public class CommonUploadRequestExecutorApacheImpl
|
||||
extends CommonUploadRequestExecutor<CloseableHttpClient, HttpHost> {
|
||||
|
||||
public CommonUploadRequestExecutorApacheImpl(RequestHttp<CloseableHttpClient, HttpHost> requestHttp) {
|
||||
super(requestHttp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String execute(String uri, CommonUploadParam param, WxType wxType) throws WxErrorException, IOException {
|
||||
HttpPost httpPost = new HttpPost(uri);
|
||||
if (requestHttp.getRequestHttpProxy() != null) {
|
||||
RequestConfig config = RequestConfig.custom().setProxy(requestHttp.getRequestHttpProxy()).build();
|
||||
httpPost.setConfig(config);
|
||||
}
|
||||
if (param != null) {
|
||||
CommonUploadData data = param.getData();
|
||||
InnerStreamBody part = new InnerStreamBody(data.getInputStream(), ContentType.DEFAULT_BINARY, data.getFileName(), data.getLength());
|
||||
HttpEntity entity = MultipartEntityBuilder
|
||||
.create()
|
||||
.addPart(param.getName(), part)
|
||||
.setMode(HttpMultipartMode.RFC6532)
|
||||
.build();
|
||||
httpPost.setEntity(entity);
|
||||
}
|
||||
try (CloseableHttpResponse response = requestHttp.getRequestHttpClient().execute(httpPost)) {
|
||||
String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response);
|
||||
if (responseContent == null || responseContent.isEmpty()) {
|
||||
throw new WxErrorException(String.format("上传失败,服务器响应空 url:%s param:%s", uri, param));
|
||||
}
|
||||
WxError error = WxError.fromJson(responseContent, wxType);
|
||||
if (error.getErrorCode() != 0) {
|
||||
throw new WxErrorException(error);
|
||||
}
|
||||
return responseContent;
|
||||
} finally {
|
||||
httpPost.releaseConnection();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 内部流 请求体
|
||||
*/
|
||||
@Getter
|
||||
public static class InnerStreamBody extends InputStreamBody {
|
||||
|
||||
private final long contentLength;
|
||||
|
||||
public InnerStreamBody(final InputStream in, final ContentType contentType, final String filename, long contentLength) {
|
||||
super(in, contentType, filename);
|
||||
this.contentLength = contentLength;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package me.chanjar.weixin.common.executor;
|
||||
|
||||
import jodd.http.HttpConnectionProvider;
|
||||
import jodd.http.HttpRequest;
|
||||
import jodd.http.HttpResponse;
|
||||
import jodd.http.ProxyInfo;
|
||||
import jodd.http.upload.Uploadable;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.SneakyThrows;
|
||||
import me.chanjar.weixin.common.bean.CommonUploadData;
|
||||
import me.chanjar.weixin.common.bean.CommonUploadParam;
|
||||
import me.chanjar.weixin.common.enums.WxType;
|
||||
import me.chanjar.weixin.common.error.WxError;
|
||||
import me.chanjar.weixin.common.error.WxErrorException;
|
||||
import me.chanjar.weixin.common.util.http.RequestHttp;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* JoddHttp 通用文件上传器
|
||||
*
|
||||
* @author <a href="https://www.sacoc.cn">广州跨界</a>
|
||||
* created on 2024/01/11
|
||||
*/
|
||||
public class CommonUploadRequestExecutorJoddHttpImpl extends CommonUploadRequestExecutor<HttpConnectionProvider, ProxyInfo> {
|
||||
|
||||
public CommonUploadRequestExecutorJoddHttpImpl(RequestHttp<HttpConnectionProvider, ProxyInfo> requestHttp) {
|
||||
super(requestHttp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String execute(String uri, CommonUploadParam param, WxType wxType) throws WxErrorException, IOException {
|
||||
HttpRequest request = HttpRequest.post(uri);
|
||||
if (requestHttp.getRequestHttpProxy() != null) {
|
||||
requestHttp.getRequestHttpClient().useProxy(requestHttp.getRequestHttpProxy());
|
||||
}
|
||||
request.withConnectionProvider(requestHttp.getRequestHttpClient());
|
||||
request.form(param.getName(), new CommonUploadParamToUploadableAdapter(param.getData()));
|
||||
HttpResponse response = request.send();
|
||||
response.charset(StandardCharsets.UTF_8.name());
|
||||
String responseContent = response.bodyText();
|
||||
if (responseContent.isEmpty()) {
|
||||
throw new WxErrorException(String.format("上传失败,服务器响应空 url:%s param:%s", uri, param));
|
||||
}
|
||||
WxError error = WxError.fromJson(responseContent, wxType);
|
||||
if (error.getErrorCode() != 0) {
|
||||
throw new WxErrorException(error);
|
||||
}
|
||||
return responseContent;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通用上传参数 到 Uploadable 的适配器
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public static class CommonUploadParamToUploadableAdapter implements Uploadable<CommonUploadData> {
|
||||
|
||||
private CommonUploadData content;
|
||||
|
||||
@SneakyThrows
|
||||
@Override
|
||||
public byte[] getBytes() {
|
||||
return content.readAllBytes();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFileName() {
|
||||
return content.getFileName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMimeType() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@Override
|
||||
public int getSize() {
|
||||
return (int) content.getLength();
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream openInputStream() {
|
||||
return content.getInputStream();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package me.chanjar.weixin.common.executor;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import me.chanjar.weixin.common.bean.CommonUploadData;
|
||||
import me.chanjar.weixin.common.bean.CommonUploadParam;
|
||||
import me.chanjar.weixin.common.enums.WxType;
|
||||
import me.chanjar.weixin.common.error.WxError;
|
||||
import me.chanjar.weixin.common.error.WxErrorException;
|
||||
import me.chanjar.weixin.common.util.http.RequestHttp;
|
||||
import me.chanjar.weixin.common.util.http.okhttp.OkHttpProxyInfo;
|
||||
import okhttp3.*;
|
||||
import okio.BufferedSink;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* OkHttp 通用文件上传器
|
||||
*
|
||||
* @author <a href="https://www.sacoc.cn">广州跨界</a>
|
||||
* created on 2024/01/11
|
||||
*/
|
||||
public class CommonUploadRequestExecutorOkHttpImpl extends CommonUploadRequestExecutor<OkHttpClient, OkHttpProxyInfo> {
|
||||
|
||||
public CommonUploadRequestExecutorOkHttpImpl(RequestHttp<OkHttpClient, OkHttpProxyInfo> requestHttp) {
|
||||
super(requestHttp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String execute(String uri, CommonUploadParam param, WxType wxType) throws WxErrorException, IOException {
|
||||
RequestBody requestBody = new CommonUpdateDataToRequestBodyAdapter(param.getData());
|
||||
RequestBody body = new MultipartBody.Builder()
|
||||
.setType(MediaType.get("multipart/form-data"))
|
||||
.addFormDataPart(param.getName(), param.getData().getFileName(), requestBody)
|
||||
.build();
|
||||
Request request = new Request.Builder().url(uri).post(body).build();
|
||||
|
||||
try (Response response = requestHttp.getRequestHttpClient().newCall(request).execute()) {
|
||||
ResponseBody responseBody = response.body();
|
||||
String responseContent = responseBody == null ? "" : responseBody.string();
|
||||
if (responseContent.isEmpty()) {
|
||||
throw new WxErrorException(String.format("上传失败,服务器响应空 url:%s param:%s", uri, param));
|
||||
}
|
||||
WxError error = WxError.fromJson(responseContent, wxType);
|
||||
if (error.getErrorCode() != 0) {
|
||||
throw new WxErrorException(error);
|
||||
}
|
||||
return responseContent;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通用上传输入 到 OkHttp 请求提 适配器
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
public static class CommonUpdateDataToRequestBodyAdapter extends RequestBody {
|
||||
|
||||
private static final MediaType CONTENT_TYPE = MediaType.get("application/octet-stream");
|
||||
|
||||
private CommonUploadData data;
|
||||
|
||||
@Override
|
||||
public long contentLength() {
|
||||
return data.getLength();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public MediaType contentType() {
|
||||
return CONTENT_TYPE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(@NotNull BufferedSink bufferedSink) throws IOException {
|
||||
InputStream inputStream = data.getInputStream();
|
||||
int count;
|
||||
byte[] buffer = new byte[4096];
|
||||
while ((count = inputStream.read(buffer)) != -1) {
|
||||
bufferedSink.write(buffer, 0, count);
|
||||
}
|
||||
inputStream.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOneShot() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package me.chanjar.weixin.common.service;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import me.chanjar.weixin.common.bean.CommonUploadParam;
|
||||
import me.chanjar.weixin.common.bean.ToJson;
|
||||
import me.chanjar.weixin.common.error.WxErrorException;
|
||||
|
||||
@@ -60,4 +61,14 @@ public interface WxService {
|
||||
* @throws WxErrorException 异常
|
||||
*/
|
||||
String post(String url, ToJson obj) throws WxErrorException;
|
||||
|
||||
/**
|
||||
* 当本Service没有实现某个上传API的时候,可以用这个,针对所有微信API中的POST文件上传请求
|
||||
*
|
||||
* @param url 请求接口地址
|
||||
* @param param 文件上传对象
|
||||
* @return 接口响应字符串
|
||||
* @throws WxErrorException 异常
|
||||
*/
|
||||
String upload(String url, CommonUploadParam param) throws WxErrorException;
|
||||
}
|
||||
|
||||
@@ -1,21 +1,27 @@
|
||||
package me.chanjar.weixin.common.util.http;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import me.chanjar.weixin.common.enums.WxType;
|
||||
import me.chanjar.weixin.common.bean.CommonUploadParam;
|
||||
import me.chanjar.weixin.common.bean.result.WxMediaUploadResult;
|
||||
import me.chanjar.weixin.common.enums.WxType;
|
||||
import me.chanjar.weixin.common.error.WxErrorException;
|
||||
import me.chanjar.weixin.common.service.WxService;
|
||||
import me.chanjar.weixin.common.util.http.apache.ApacheMediaUploadRequestExecutor;
|
||||
import me.chanjar.weixin.common.util.http.jodd.JoddHttpMediaUploadRequestExecutor;
|
||||
import me.chanjar.weixin.common.util.http.okhttp.OkHttpMediaUploadRequestExecutor;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* 上传媒体文件请求执行器.
|
||||
* 请求的参数是File, 返回的结果是String
|
||||
*
|
||||
* @author Daniel Qian
|
||||
* @see WxService#upload(String, CommonUploadParam) 通用的上传,封装接口是推荐调用此方法
|
||||
* @see CommonUploadParam 通用的上传参数
|
||||
* @deprecated 不应该继续使用执行器的方式上传文件,封装上传接口时应调用通用的文件上传,而旧代码也应该逐步迁移为新的上传方式
|
||||
*/
|
||||
@Deprecated
|
||||
public abstract class MediaUploadRequestExecutor<H, P> implements RequestExecutor<WxMediaUploadResult, File> {
|
||||
protected RequestHttp<H, P> requestHttp;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user