From 41853727462773a21da5d3750071a92caaf6b667 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=B3=89=E4=BD=B3?= <1107186916@qq.com> Date: Thu, 24 Sep 2026 22:51:30 +0800 Subject: [PATCH] =?UTF-8?q?:bug:=20Feign=E6=97=A5=E5=BF=97multipart?= =?UTF-8?q?=E8=AF=B7=E6=B1=82=E8=A7=A3=E6=9E=90=E9=9D=9E=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E5=AD=97=E6=AE=B5=E6=89=93=E5=8D=B0=EF=BC=8C=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E9=83=A8=E5=88=86=E7=94=A8=E5=8D=A0=E4=BD=8D=E7=AC=A6=E6=9B=BF?= =?UTF-8?q?=E4=BB=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/feign/CustomFeignLogger.java | 85 ++++++++++++++++++- 1 file changed, 81 insertions(+), 4 deletions(-) diff --git a/blade-common/src/main/java/org/springblade/common/feign/CustomFeignLogger.java b/blade-common/src/main/java/org/springblade/common/feign/CustomFeignLogger.java index cf52849e..d500c76c 100644 --- a/blade-common/src/main/java/org/springblade/common/feign/CustomFeignLogger.java +++ b/blade-common/src/main/java/org/springblade/common/feign/CustomFeignLogger.java @@ -33,8 +33,12 @@ import lombok.extern.slf4j.Slf4j; import java.io.IOException; import java.nio.charset.StandardCharsets; +import java.util.ArrayList; import java.util.Collection; +import java.util.List; import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** * 自定义Feign日志记录器,只记录出入参信息 @@ -62,10 +66,13 @@ public class CustomFeignLogger extends Logger { Map> headers = request.headers(); byte[] body = request.body(); + String contentType = getContentType(headers); String bodyStr; if (body == null || body.length == 0) { bodyStr = "[无]"; - } else if (isBinaryContent(getContentType(headers))) { + } else if (isMultipartContent(contentType)) { + bodyStr = parseMultipartBody(body, contentType); + } else if (isBinaryContent(contentType)) { bodyStr = "[二进制数据,长度=" + body.length + "字节]"; } else { bodyStr = new String(body, StandardCharsets.UTF_8); @@ -122,7 +129,7 @@ public class CustomFeignLogger extends Logger { } /** - * 判断是否为二进制内容或不应打印的内容(如文件上传) + * 判断是否为二进制内容 */ private boolean isBinaryContent(String contentType) { if (contentType == null || contentType.isEmpty()) { @@ -132,7 +139,77 @@ public class CustomFeignLogger extends Logger { return lowerContentType.startsWith(BINARY_CONTENT_TYPE_PREFIX) || lowerContentType.startsWith(IMAGE_CONTENT_TYPE_PREFIX) || lowerContentType.startsWith(AUDIO_CONTENT_TYPE_PREFIX) - || lowerContentType.startsWith(VIDEO_CONTENT_TYPE_PREFIX) - || lowerContentType.startsWith(MULTIPART_CONTENT_TYPE_PREFIX); + || lowerContentType.startsWith(VIDEO_CONTENT_TYPE_PREFIX); + } + + /** + * 判断是否为multipart内容 + */ + private boolean isMultipartContent(String contentType) { + return contentType != null && contentType.toLowerCase().startsWith(MULTIPART_CONTENT_TYPE_PREFIX); + } + + /** + * 解析multipart请求体,文件部分用占位符替代,文本字段保留打印 + */ + private String parseMultipartBody(byte[] body, String contentType) { + String boundary = extractBoundary(contentType); + if (boundary == null) { + return "[multipart数据,长度=" + body.length + "字节]"; + } + String bodyStr = new String(body, StandardCharsets.UTF_8); + String delimiter = "--" + boundary; + String[] parts = bodyStr.split(delimiter); + + List fields = new ArrayList<>(); + for (String part : parts) { + if (part.isEmpty() || part.startsWith("--") || part.trim().isEmpty()) { + continue; + } + int headerEnd = part.indexOf("\r\n\r\n"); + if (headerEnd < 0) { + continue; + } + String headers = part.substring(0, headerEnd); + String value = part.substring(headerEnd + 4); + if (value.endsWith("\r\n")) { + value = value.substring(0, value.length() - 2); + } + + String name = extractParam(headers, "name"); + + if (headers.contains("filename=")) { + String filename = extractParam(headers, "filename"); + fields.add(name + "=[文件: " + filename + "]"); + } else { + fields.add(name + "=" + value); + } + } + return fields.isEmpty() ? "[multipart数据]" : "{" + String.join(", ", fields) + "}"; + } + + /** + * 从Content-Type中提取boundary + */ + private String extractBoundary(String contentType) { + for (String param : contentType.split(";")) { + param = param.trim(); + if (param.toLowerCase().startsWith("boundary=")) { + return param.substring("boundary=".length()).trim(); + } + } + return null; + } + + /** + * 从headers中提取参数值(如name、filename) + */ + private String extractParam(String headers, String paramName) { + Pattern pattern = Pattern.compile(paramName + "=\"([^\"]*)\""); + Matcher matcher = pattern.matcher(headers); + if (matcher.find()) { + return matcher.group(1); + } + return paramName; } }