设置下载时的默认文件名
🏷️ EasyExcel
今天用 EasyExcel 导出 Excel 文件,通过 ApiFox 调用接口保存时默认文件名为 response.zip ,于是查了下如何在响应中设置默认文件名。
经调查,默认的文件名是通过 Header 中 Content-Disposition
的值决定的,其格式为
http
Content-Disposition: <disposition-type>; <parameter-name>="<parameter-value>"; ...
其中 <parameter-value>
的值通常需要 URL 编码。
如果是下载文件,则 <disposition-type>
值为 attachment :
http
Content-Disposition: attachment; filename="example.txt"
如果文件名中含有中文等特殊字符,则需要改成如下格式:
http
Content-Disposition: attachment; filename*=UTF-8''%E6%B5%8B%E8%AF%95%E6%96%87%E4%BB%B6.txt
filename*=
参数指定编码类型和编码值。UTF-8
表示使用 UTF-8 编码''
表示空格分隔符%E6%B5%8B%E8%AF%95%E6%96%87%E4%BB%B6.txt
是文件名的编码值
在 Java 中可以通过 ContentDisposition
对象构建这个字符串:
java
ContentDisposition disposition = ContentDisposition.builder("attachment")
.filename("测试文件.txt", StandardCharsets.UTF_8)
.build();
System.out.println(disposition.toString());
奇怪的是,在 ApiFox 中不支持这种,反而支持不做 URL 编码的格式:
http
Content-Disposition: attachment; filename="测试文件.txt"
为了兼容,最后将其修改为了如下格式:
http
Content-Disposition: attachment; filename="测试文件.txt"; filename*=UTF-8''%E6%B5%8B%E8%AF%95%E6%96%87%E4%BB%B6.txt
另外每种文件对应的 Content-Type
是不一样的,比如 xls 文件对应的是 application/vnd.ms-excel
,pdf 对应的是 application/pdf
。
可以参考下 org.springframework.boot.web.server.MimeMappings.DEFAULT
中的值,不过这里的并不全。
最终的代码如下:
java
import cn.hutool.core.io.file.FileNameUtil;
import org.springframework.boot.web.server.MimeMappings;
import org.springframework.http.HttpHeaders;
import javax.servlet.http.HttpServletResponse;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Optional;
public class HttpUtil {
public static void setDownloadFileName(HttpServletResponse response, String filename) throws UnsupportedEncodingException {
// 设置 Content-Disposition
String encodedFilename = URLEncoder.encode(filename, StandardCharsets.UTF_8.toString())
// 替换空格(+)为 %20
.replace("+", "%20");
String disposition = "attachment; "
+ "filename=\"" + encodedFilename + "\"; "
+ "filename*=UTF-8''" + encodedFilename;
response.setHeader(HttpHeaders.CONTENT_DISPOSITION, disposition);
response.setHeader(HttpHeaders.CONTENT_TYPE, getContentTypeByFilename(filename));
}
public static String getContentTypeByFilename(String filename) {
return Optional.ofNullable(MimeMappings.DEFAULT.get(FileNameUtil.getSuffix(filename)))
// 默认二进制流
.orElse("application/octet-stream");
}
}
在 EasyExcel 的导出处理前调用上面的 setDownloadFileName
方法:
java
// 设置文件名
String sheetName = "消费明细";
String fileName = String.format("%s_%s.xls", sheetName, LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")));
HttpUtil.setDownloadFileName(response, fileName);
// 导出文件
EasyExcel.write(response.getOutputStream(), BrandBillDetailVO.class)
.excelType(ExcelTypeEnum.XLS)
.sheet(sheetName)
.doWrite(bills);