add HttpHeaderUtil

This commit is contained in:
Looly 2024-08-15 18:46:49 +08:00
parent 85c685b46a
commit 44da9c3488
2 changed files with 86 additions and 12 deletions

View File

@ -18,18 +18,21 @@ package org.dromara.hutool.http.meta;
import org.dromara.hutool.core.collection.CollUtil; import org.dromara.hutool.core.collection.CollUtil;
import org.dromara.hutool.core.map.CaseInsensitiveMap; import org.dromara.hutool.core.map.CaseInsensitiveMap;
import org.dromara.hutool.core.net.url.UrlDecoder;
import org.dromara.hutool.core.regex.ReUtil; import org.dromara.hutool.core.regex.ReUtil;
import org.dromara.hutool.core.text.StrUtil; import org.dromara.hutool.core.text.StrUtil;
import org.dromara.hutool.core.text.split.SplitUtil; import org.dromara.hutool.core.text.split.SplitUtil;
import org.dromara.hutool.core.util.CharsetUtil;
import org.dromara.hutool.core.util.ObjUtil; import org.dromara.hutool.core.util.ObjUtil;
import java.io.Serializable;
import java.nio.charset.Charset;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
/** /**
* HTTP头相关方法<br> * HTTP头相关方法<br>
* 相关规范见https://www.rfc-editor.org/rfc/rfc5987 * 相关规范见https://www.rfc-editor.org/rfc/rfc5987
* TODO Rfc5987 extended value需要单独解析
* *
* @author Looly * @author Looly
* @since 6.0.0 * @since 6.0.0
@ -61,7 +64,7 @@ public class HttpHeaderUtil {
* 按照规范`Content-Disposition`可能返回多个此处遍历所有返回头并且`filename*`始终优先获取即使`filename`存在并更靠前<br> * 按照规范`Content-Disposition`可能返回多个此处遍历所有返回头并且`filename*`始终优先获取即使`filename`存在并更靠前<br>
* 参考https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Content-Disposition * 参考https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Content-Disposition
* *
* @param headers 头列表 * @param headers 头列表
* @param paramName 文件参数名如果为{@code null}则使用默认的`filename` * @param paramName 文件参数名如果为{@code null}则使用默认的`filename`
* @return 文件名empty表示无 * @return 文件名empty表示无
*/ */
@ -104,22 +107,93 @@ public class HttpHeaderUtil {
* 获取rfc5987标准的值标准见https://www.rfc-editor.org/rfc/rfc5987#section-3.2.1<br> * 获取rfc5987标准的值标准见https://www.rfc-editor.org/rfc/rfc5987#section-3.2.1<br>
* 包括 * 包括
* *
*<ul> * <ul>
* <li>Non-extended无双引号包裹的值</li> * <li>Non-extended无双引号包裹的值</li>
* <li>Non-extended双引号包裹的值</li> * <li>Non-extended双引号包裹的值</li>
* <li>Extended notation编码'语言'</li> * <li>Extended notation编码'语言'</li>
*</ul> * </ul>
* *
* @param value * @param value
* @return 结果值 * @return 结果值
*/ */
private static String getRfc5987Value(final String value){ private static String getRfc5987Value(final String value) {
final List<String> split = SplitUtil.split(value, "'"); return ExtendedValue.of(value).getDecodeValue();
if(3 == split.size()){ }
return split.get(2);
/**
* 根据rfc5987的扩展值格式为
* <pre>{@code
* 编码'语言'
* }</pre>
*/
public static class ExtendedValue implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 创建扩展值
*
* @param value 扩展值或普通值
* @return 扩展值
*/
public static ExtendedValue of(final String value) {
return new ExtendedValue(value);
} }
// 普通值 private Charset charset;
return StrUtil.unWrap(value, '"'); private String language;
private final String value;
/**
* 构造
*
* @param value 扩展值或普通值
*/
public ExtendedValue(final String value) {
final List<String> split = SplitUtil.split(value, "'");
if (3 == split.size()) {
this.charset = CharsetUtil.charset(split.get(0));
this.language = split.get(1);
this.value = split.get(2);
} else {
this.value = StrUtil.unWrap(value, '"');
;
}
}
/**
* 获取编码无定义为{@code null}
*
* @return 编码无定义为{@code null}
*/
public Charset getCharset() {
return charset;
}
/**
* 获取语言
*
* @return 语言
*/
public String getLanguage() {
return language;
}
/**
* 获取原始值
*
* @return 原始值
*/
public String getValue() {
return value;
}
/**
* 获取解码后的值
*
* @return 解码后的值
*/
public String getDecodeValue() {
return UrlDecoder.decode(value, charset);
}
} }
} }

View File

@ -15,7 +15,7 @@ public class HttpHeaderUtilTest {
headers.put(HeaderName.CONTENT_DISPOSITION.getValue(), headers.put(HeaderName.CONTENT_DISPOSITION.getValue(),
ListUtil.of("attachment; filename*=utf-8''%E6%B5%8B%E8%AF%95.xlsx; filename=\"æµ\u008Bè¯\u0095.xlsx\"")); ListUtil.of("attachment; filename*=utf-8''%E6%B5%8B%E8%AF%95.xlsx; filename=\"æµ\u008Bè¯\u0095.xlsx\""));
final String fileNameFromDisposition = HttpHeaderUtil.getFileNameFromDisposition(headers, null); final String fileNameFromDisposition = HttpHeaderUtil.getFileNameFromDisposition(headers, null);
Assertions.assertEquals("%E6%B5%8B%E8%AF%95.xlsx", fileNameFromDisposition); Assertions.assertEquals("测试.xlsx", fileNameFromDisposition);
} }
@Test @Test
@ -24,7 +24,7 @@ public class HttpHeaderUtilTest {
headers.put(HeaderName.CONTENT_DISPOSITION.getValue(), headers.put(HeaderName.CONTENT_DISPOSITION.getValue(),
ListUtil.of("attachment; filename*=utf-8''%E6%B5%8B%E8%AF%95.xlsx")); ListUtil.of("attachment; filename*=utf-8''%E6%B5%8B%E8%AF%95.xlsx"));
final String fileNameFromDisposition = HttpHeaderUtil.getFileNameFromDisposition(headers, null); final String fileNameFromDisposition = HttpHeaderUtil.getFileNameFromDisposition(headers, null);
Assertions.assertEquals("%E6%B5%8B%E8%AF%95.xlsx", fileNameFromDisposition); Assertions.assertEquals("测试.xlsx", fileNameFromDisposition);
} }
@Test @Test