diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c0263cdd..3c722f69b 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ # 🚀Changelog ------------------------------------------------------------------------------------------------------------- -# 5.8.28(2024-05-17) +# 5.8.28(2024-05-20) ### 🐣新特性 * 【core 】 修正XmlUtil的omitXmlDeclaration描述注释(issue#I9CPC7@Gitee) @@ -22,6 +22,7 @@ * 【core 】 增加IdConstants,提高Snowflake初始化性能(issue#3581@Github) * 【core 】 优化 CharSequenceUtil工具类 startWithAny()、startWithAnyIgnoreCase() 参数命名错误问题(pr#1219@Gitee) * 【core 】 ListUtil.setOrPadding增加重载,可选限制index大小(issue#3586@Github) +* 【http 】 getFileNameFromDisposition更加规范,从多个头的值中获取,且`filename*`优先级更高(pr#3590@Gitee) ### 🐞Bug修复 * 【http 】 修复HttpUtil.urlWithFormUrlEncoded方法重复编码问题(issue#3536@Github) diff --git a/hutool-core/src/main/java/cn/hutool/core/util/ModifierUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/ModifierUtil.java index 10c6836f2..5364d2f8b 100644 --- a/hutool-core/src/main/java/cn/hutool/core/util/ModifierUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/util/ModifierUtil.java @@ -319,10 +319,10 @@ public class ModifierUtil { //-------------------------------------------------------------------------------------------------------- Private method start /** - * 多个修饰符做“与”操作,表示同时存在多个修饰符 + * 多个修饰符做“或”操作,表示同时存在多个修饰符 * * @param modifierTypes 修饰符列表,元素不能为空 - * @return “与”之后的修饰符 + * @return “或”之后的修饰符 */ private static int modifiersToInt(ModifierType... modifierTypes) { int modifier = modifierTypes[0].getValue(); diff --git a/hutool-http/src/main/java/cn/hutool/http/HttpResponse.java b/hutool-http/src/main/java/cn/hutool/http/HttpResponse.java index 96ec991c3..74898f2c2 100755 --- a/hutool-http/src/main/java/cn/hutool/http/HttpResponse.java +++ b/hutool-http/src/main/java/cn/hutool/http/HttpResponse.java @@ -1,5 +1,6 @@ package cn.hutool.http; +import cn.hutool.core.collection.CollUtil; import cn.hutool.core.convert.Convert; import cn.hutool.core.io.FastByteArrayOutputStream; import cn.hutool.core.io.FileUtil; @@ -467,25 +468,61 @@ public class HttpResponse extends HttpBase implements Closeable { /** * 从Content-Disposition头中获取文件名 - * @param paramName 文件参数名 * * @return 文件名,empty表示无 */ + public String getFileNameFromDisposition() { + return getFileNameFromDisposition(null); + } + + /** + * 从Content-Disposition头中获取文件名,以参数名为`filename`为例,规则为: + * + * 按照规范,`Content-Disposition`可能返回多个,此处遍历所有返回头,并且`filename*`始终优先获取,即使`filename`存在并更靠前。
+ * 参考:https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Content-Disposition + * + * @param paramName 文件参数名,如果为{@code null}则使用默认的`filename` + * @return 文件名,empty表示无 + */ public String getFileNameFromDisposition(String paramName) { paramName = ObjUtil.defaultIfNull(paramName, "filename"); + final List dispositions = headerList(Header.CONTENT_DISPOSITION.name()); String fileName = null; - final String disposition = header(Header.CONTENT_DISPOSITION); - if (StrUtil.isNotBlank(disposition)) { - fileName = ReUtil.get(paramName+"=\"(.*?)\"", disposition, 1); - if (StrUtil.isBlank(fileName)) { - fileName = StrUtil.subAfter(disposition, paramName + "=", true); + if (CollUtil.isNotEmpty(dispositions)) { + + // filename* 采用了 RFC 5987 中规定的编码方式,优先读取 + fileName = getFileNameFromDispositions(dispositions, StrUtil.addSuffixIfNot(paramName, "*")); + if ((!StrUtil.endWith(fileName, "*")) && StrUtil.isBlank(fileName)) { + fileName = getFileNameFromDispositions(dispositions, paramName); } } + return fileName; } // ---------------------------------------------------------------- Private method start + /** + * 从Content-Disposition头中获取文件名 + * + * @param dispositions Content-Disposition头列表 + * @param paramName 文件参数名 + * @return 文件名,empty表示无 + */ + private static String getFileNameFromDispositions(final List dispositions, String paramName) { + String fileName = null; + for (String disposition : dispositions) { + fileName = ReUtil.getGroup1(paramName + "=\"(.*?)\"", disposition); + if (StrUtil.isNotBlank(fileName)) { + break; + } + } + return fileName; + } + /** * 初始化Http响应,并在报错时关闭连接。
* 初始化包括: @@ -632,7 +669,7 @@ public class HttpResponse extends HttpBase implements Closeable { } catch (IORuntimeException e) { //noinspection StatementWithEmptyBody if (isIgnoreEOFError - && (e.getCause() instanceof EOFException || StrUtil.containsIgnoreCase(e.getMessage(), "Premature EOF"))) { + && (e.getCause() instanceof EOFException || StrUtil.containsIgnoreCase(e.getMessage(), "Premature EOF"))) { // 忽略读取HTTP流中的EOF错误 } else { throw e;