mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-04-19 03:01:48 +08:00
getFileNameFromDisposition更加规范,从多个头的值中获取,且filename*
优先级更高
This commit is contained in:
parent
619d5ca61c
commit
49c891cb50
@ -19,6 +19,7 @@ import org.dromara.hutool.http.HttpException;
|
|||||||
import org.dromara.hutool.http.client.body.ResponseBody;
|
import org.dromara.hutool.http.client.body.ResponseBody;
|
||||||
import org.dromara.hutool.http.meta.ContentTypeUtil;
|
import org.dromara.hutool.http.meta.ContentTypeUtil;
|
||||||
import org.dromara.hutool.http.meta.HeaderName;
|
import org.dromara.hutool.http.meta.HeaderName;
|
||||||
|
import org.dromara.hutool.http.meta.HttpHeaderUtil;
|
||||||
|
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -136,6 +137,16 @@ public interface Response extends Closeable {
|
|||||||
return header(name.toString());
|
return header(name.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据name获取对应的头信息列表
|
||||||
|
*
|
||||||
|
* @param name Header名
|
||||||
|
* @return Header值
|
||||||
|
*/
|
||||||
|
default List<String> headerList(final String name) {
|
||||||
|
return HttpHeaderUtil.headerList(headers(), name);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取内容编码
|
* 获取内容编码
|
||||||
*
|
*
|
||||||
@ -172,8 +183,7 @@ public interface Response extends Closeable {
|
|||||||
* @since 4.6.2
|
* @since 4.6.2
|
||||||
*/
|
*/
|
||||||
default boolean isChunked() {
|
default boolean isChunked() {
|
||||||
final String transferEncoding = header(HeaderName.TRANSFER_ENCODING);
|
return "Chunked".equalsIgnoreCase(header(HeaderName.TRANSFER_ENCODING));
|
||||||
return "Chunked".equalsIgnoreCase(transferEncoding);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -186,6 +196,22 @@ public interface Response extends Closeable {
|
|||||||
return header(HeaderName.SET_COOKIE);
|
return header(HeaderName.SET_COOKIE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从Content-Disposition头中获取文件名,以参数名为`filename`为例,规则为:
|
||||||
|
* <ul>
|
||||||
|
* <li>首先按照RFC5987规范检查`filename*`参数对应的值,即:`filename*="example.txt"`,则获取`example.txt`</li>
|
||||||
|
* <li>如果找不到`filename*`参数,则检查`filename`参数对应的值,即:`filename="example.txt"`,则获取`example.txt`</li>
|
||||||
|
* </ul>
|
||||||
|
* 按照规范,`Content-Disposition`可能返回多个,此处遍历所有返回头,并且`filename*`始终优先获取,即使`filename`存在并更靠前。<br>
|
||||||
|
* 参考:https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Content-Disposition
|
||||||
|
*
|
||||||
|
* @param paramName 文件参数名,如果为{@code null}则使用默认的`filename`
|
||||||
|
* @return 文件名,empty表示无
|
||||||
|
*/
|
||||||
|
default String getFileNameFromDisposition(final String paramName) {
|
||||||
|
return HttpHeaderUtil.getFileNameFromDisposition(headers(), paramName);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 链式处理结果
|
* 链式处理结果
|
||||||
*
|
*
|
||||||
|
@ -12,26 +12,20 @@
|
|||||||
|
|
||||||
package org.dromara.hutool.http.client.body;
|
package org.dromara.hutool.http.client.body;
|
||||||
|
|
||||||
import org.dromara.hutool.core.io.file.FileUtil;
|
|
||||||
import org.dromara.hutool.core.io.IoUtil;
|
import org.dromara.hutool.core.io.IoUtil;
|
||||||
import org.dromara.hutool.core.io.StreamProgress;
|
import org.dromara.hutool.core.io.StreamProgress;
|
||||||
import org.dromara.hutool.core.io.file.FileNameUtil;
|
import org.dromara.hutool.core.io.file.FileNameUtil;
|
||||||
|
import org.dromara.hutool.core.io.file.FileUtil;
|
||||||
import org.dromara.hutool.core.io.stream.SyncInputStream;
|
import org.dromara.hutool.core.io.stream.SyncInputStream;
|
||||||
import org.dromara.hutool.core.lang.Assert;
|
import org.dromara.hutool.core.lang.Assert;
|
||||||
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.util.ObjUtil;
|
|
||||||
import org.dromara.hutool.http.HttpException;
|
import org.dromara.hutool.http.HttpException;
|
||||||
import org.dromara.hutool.http.HttpGlobalConfig;
|
import org.dromara.hutool.http.HttpGlobalConfig;
|
||||||
import org.dromara.hutool.http.client.Response;
|
import org.dromara.hutool.http.client.Response;
|
||||||
import org.dromara.hutool.http.html.HtmlUtil;
|
import org.dromara.hutool.http.html.HtmlUtil;
|
||||||
import org.dromara.hutool.http.meta.HeaderName;
|
import org.dromara.hutool.http.meta.HeaderName;
|
||||||
|
|
||||||
import java.io.Closeable;
|
import java.io.*;
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 响应体部分封装
|
* 响应体部分封装
|
||||||
@ -234,30 +228,11 @@ public class ResponseBody implements HttpBody, Closeable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 从头信息中获取文件名
|
// 从头信息中获取文件名
|
||||||
final String fileName = getFileNameFromDisposition(ObjUtil.defaultIfNull(customParamName, "filename"));
|
final String fileName = response.getFileNameFromDisposition(customParamName);
|
||||||
if (StrUtil.isBlank(fileName)) {
|
if (StrUtil.isBlank(fileName)) {
|
||||||
throw new HttpException("Can`t get file name from [Content-Disposition]!");
|
throw new HttpException("Can`t get file name from [Content-Disposition]!");
|
||||||
}
|
}
|
||||||
return FileUtil.file(targetFileOrDir, fileName);
|
return FileUtil.file(targetFileOrDir, fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 从Content-Disposition头中获取文件名
|
|
||||||
*
|
|
||||||
* @param paramName 文件名的参数名
|
|
||||||
* @return 文件名,empty表示无
|
|
||||||
* @since 5.8.10
|
|
||||||
*/
|
|
||||||
private String getFileNameFromDisposition(final String paramName) {
|
|
||||||
String fileName = null;
|
|
||||||
final String disposition = response.header(HeaderName.CONTENT_DISPOSITION);
|
|
||||||
if (StrUtil.isNotBlank(disposition)) {
|
|
||||||
fileName = ReUtil.get(paramName + "=\"(.*?)\"", disposition, 1);
|
|
||||||
if (StrUtil.isBlank(fileName)) {
|
|
||||||
fileName = StrUtil.subAfter(disposition, paramName + "=", true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return fileName;
|
|
||||||
}
|
|
||||||
// endregion ---------------------------------------------------------------------------- Private Methods
|
// endregion ---------------------------------------------------------------------------- Private Methods
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,94 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024. looly(loolly@aliyun.com)
|
||||||
|
* Hutool is licensed under Mulan PSL v2.
|
||||||
|
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
|
* You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
* https://license.coscl.org.cn/MulanPSL2
|
||||||
|
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||||
|
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
|
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
|
* See the Mulan PSL v2 for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.dromara.hutool.http.meta;
|
||||||
|
|
||||||
|
import org.dromara.hutool.core.collection.CollUtil;
|
||||||
|
import org.dromara.hutool.core.map.CaseInsensitiveMap;
|
||||||
|
import org.dromara.hutool.core.regex.ReUtil;
|
||||||
|
import org.dromara.hutool.core.text.StrUtil;
|
||||||
|
import org.dromara.hutool.core.util.ObjUtil;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP头相关方法
|
||||||
|
*
|
||||||
|
* @author Looly
|
||||||
|
* @since 6.0.0
|
||||||
|
*/
|
||||||
|
public class HttpHeaderUtil {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据name获取对应的头信息列表
|
||||||
|
*
|
||||||
|
* @param headers 头列表
|
||||||
|
* @param name Header名
|
||||||
|
* @return Header值
|
||||||
|
*/
|
||||||
|
public static List<String> headerList(final Map<String, List<String>> headers, final String name) {
|
||||||
|
if (StrUtil.isBlank(name)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final CaseInsensitiveMap<String, List<String>> headersIgnoreCase = new CaseInsensitiveMap<>(headers);
|
||||||
|
return headersIgnoreCase.get(name.trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从Content-Disposition头中获取文件名,以参数名为`filename`为例,规则为:
|
||||||
|
* <ul>
|
||||||
|
* <li>首先按照RFC5987规范检查`filename*`参数对应的值,即:`filename*="example.txt"`,则获取`example.txt`</li>
|
||||||
|
* <li>如果找不到`filename*`参数,则检查`filename`参数对应的值,即:`filename="example.txt"`,则获取`example.txt`</li>
|
||||||
|
* </ul>
|
||||||
|
* 按照规范,`Content-Disposition`可能返回多个,此处遍历所有返回头,并且`filename*`始终优先获取,即使`filename`存在并更靠前。<br>
|
||||||
|
* 参考:https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Content-Disposition
|
||||||
|
*
|
||||||
|
* @param headers 头列表
|
||||||
|
* @param paramName 文件参数名,如果为{@code null}则使用默认的`filename`
|
||||||
|
* @return 文件名,empty表示无
|
||||||
|
*/
|
||||||
|
public static String getFileNameFromDisposition(final Map<String, List<String>> headers, String paramName) {
|
||||||
|
paramName = ObjUtil.defaultIfNull(paramName, "filename");
|
||||||
|
final List<String> dispositions = headerList(headers, HeaderName.CONTENT_DISPOSITION.name());
|
||||||
|
String fileName = null;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从Content-Disposition头中获取文件名
|
||||||
|
*
|
||||||
|
* @param dispositions Content-Disposition头列表
|
||||||
|
* @param paramName 文件参数名
|
||||||
|
* @return 文件名,empty表示无
|
||||||
|
*/
|
||||||
|
private static String getFileNameFromDispositions(final List<String> dispositions, String paramName) {
|
||||||
|
String fileName = null;
|
||||||
|
for (final String disposition : dispositions) {
|
||||||
|
fileName = ReUtil.getGroup1(paramName + "=\"(.*?)\"", disposition);
|
||||||
|
if (StrUtil.isNotBlank(fileName)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fileName;
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user