1
0
mirror of synced 2025-12-14 19:23:48 +08:00

🆕 【开放平台】接入小程序认证(年审)相关接口,同时增加公共的文件上传方法

This commit is contained in:
天朝红雨
2024-01-12 20:01:42 +08:00
committed by GitHub
parent d957896ac1
commit 774579186c
39 changed files with 1353 additions and 270 deletions

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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());
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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();
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}

View File

@@ -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;