你是一名专业的java工程师,在使用springboot结合docx4j,将InputStream流转为word文档时,并将word文档下载到客户端上。通过postman下载下来的中文名称为乱码。以下是代码 InputStream htmlStream = minioUtils.downloadFileByBucketNameAndFileName("", content.split(bucketName)[1]); // 1. 读取 HTML 内容 String html = IOUtils.toString(htmlStream, StandardCharsets.UTF_8); // 2. 创建 Word 文档 WordprocessingMLPackage wordPackage = WordprocessingMLPackage.createPackage(); // 3. 导入 HTML 内容 XHTMLImporterImpl importer = new XHTMLImporterImpl(wordPackage); wordPackage.getMainDocumentPart().getContent().addAll( importer.convert(html, null) ); // 4. 保存到输出流 ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); Docx4J.save(wordPackage, outputStream, Docx4J.FLAG_SAVE_ZIP_FILE); // 3. 设置响应头(处理中文文件名) String fileName = contentMaster.getName() + ".docx"; String encodedFileName = URLEncoder.encode(fileName, "UTF-8") .replaceAll("\\+", "%20"); InputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray()); return ResponseEntity.ok() .header("Content-Disposition", "attachment; filename*=UTF-8''" + encodedFileName + ";" + // RFC5987 " filename=\"" + new String(fileName.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1) + "\"") // 传统兼容 .contentType(MediaType.APPLICATION_OCTET_STREAM) .body(new InputStreamResource(inputStream)); 给出你的解决方案
时间: 2025-07-27 20:27:49 浏览: 4
<think>我们正在解决SpringBoot结合Docx4J生成Word文档下载时中文文件名乱码问题。根据引用[1]中提到的在Linux容器中遇到的问题,特别是文件名和内容的中文问题,我们可以推测文件名乱码可能是由于HTTP响应头中文件名编码不正确导致的。在HTTP响应中,文件名通常通过Content-Disposition头设置,例如:Content-Disposition:attachment;filename="文件名.docx"但是,如果直接使用中文,不同浏览器可能因为编码问题而显示乱码。为了解决这个问题,通常需要对文件名进行编码(通常使用URL编码和UTF-8字符集),并按照RFC5987标准设置。解决方案:1.设置Content-Disposition头时,对文件名进行UTF-8编码,并采用标准格式。2.确保整个HTTP响应使用UTF-8编码。具体步骤(在SpringBoot控制器中):我们可以使用以下方法设置响应头:方法一:使用RFC5987标准(推荐)StringencodedFileName=URLEncoder.encode(originalFileName,"UTF-8").replaceAll("\\+","%20");StringcontentDisposition="attachment;filename*=UTF-8''"+encodedFileName;方法二:为了兼容不同浏览器,可以同时设置普通filename和RFC5987标准的filename*,例如:StringcontentDisposition="attachment;filename=\""+newString(originalFileName.getBytes("ISO-8859-1"),"UTF-8")+"\";filename*=UTF-8''"+encodedFileName;但是,更简单且推荐的方式是使用Spring框架提供的ContentDisposition工具类(Spring4.3+)或直接使用上述RFC5987标准。在SpringBoot2.1及以上版本,我们可以这样写:ContentDispositioncontentDisposition=ContentDisposition.builder("attachment").filename(originalFileName,StandardCharsets.UTF_8)//这个方法内部会按照RFC5987编码.build();response.setHeader(HttpHeaders.CONTENT_DISPOSITION,contentDisposition.toString());或者,如果不使用构建器,可以直接设置:StringheaderValue="attachment;filename=\""+newString(originalFileName.getBytes(StandardCharsets.UTF_8),StandardCharsets.ISO_8859_1)+"\";";//但这种方法并不总是可靠,所以建议使用构建器。然而,更简单的方式是使用Spring的ContentDisposition类(org.springframework.http.ContentDisposition)来构建。实际代码示例(在控制器方法中):@GetMapping("/download")publicResponseEntity<Resource>download()throwsException{//生成Word文档,这里假设已经生成并得到文件或输入流Filefile=...;//你的Word文件InputStreamResourceresource=newInputStreamResource(newFileInputStream(file));//设置响应头HttpHeadersheaders=newHttpHeaders();headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);ContentDispositioncontentDisposition=ContentDisposition.attachment().filename(file.getName(),StandardCharsets.UTF_8)//关键在这里,设置文件名并指定UTF-8编码.build();headers.setContentDisposition(contentDisposition);returnResponseEntity.ok().headers(headers).contentLength(file.length()).body(resource);}注意:在引用[1]中提到了设置response.setContentLength((int)file.length()),这有助于解决文件损坏问题,所以在ResponseEntity中也设置了contentLength。另外,关于引用[2]中提到的合并Word文档的代码,与文件名乱码问题无关,但请注意,如果合并后的文档内容出现中文乱码,可能是文档生成时字体设置问题,需要确保在生成Word文档时指定中文字体(如宋体、黑体等),并且容器中要有相应的字体。不过,当前问题只涉及文件名乱码。因此,重点在于设置HTTP响应头中的Content-Disposition,并按照RFC5987标准对文件名进行编码。如果使用较低版本的Spring(如4.3以下),可以手动构建字符串:StringfileName="中文文件名.docx";StringencodedFileName=URLEncoder.encode(fileName,"UTF-8").replace("+","%20");StringcontentDisposition="attachment;filename*=UTF-8''"+encodedFileName;response.setHeader("Content-Disposition",contentDisposition);同时,确保在web配置中设置了字符编码过滤器(SpringBoot默认已经配置了,但如果你有自定义,请注意)。总结:使用Spring的ContentDisposition类(在org.springframework.http包中)可以方便地设置支持UTF-8文件名的响应头,从而解决中文文件名乱码问题。注意:如果使用Servlet的HttpServletResponse,也可以这样设置:StringfileName="中文文件名.docx";StringencodedFileName=URLEncoder.encode(fileName,"UTF-8").replace("+","%20");StringcontentDisposition="attachment;filename*=UTF-8''"+encodedFileName;response.setHeader("Content-Disposition",contentDisposition);但使用ResponseEntity的方式更符合SpringMVC的编程风格。另外,关于引用[1]中提到的PDF中文问题,如果将来遇到,可能需要检查生成PDF时使用的字体是否支持中文,确保在容器中安装了中文字体。现在,我们针对文件名乱码问题给出解决方案。如果问题仍然存在,请检查客户端浏览器是否支持RFC5987标准(现代浏览器一般都支持)。最后,根据用户问题,我们生成相关问题:</think>### Spring Boot 解决 Docx4j 下载 Word 文件中文文件名乱码问题
#### 问题分析
中文文件名乱码通常由以下原因导致:
1. **HTTP 响应头编码问题**
`Content-Disposition` 头未正确声明 UTF-8 编码,导致浏览器误解析为 ISO-8859-1。
2. **Servlet 容器默认编码限制**
部分 Servlet 容器(如 Tomcat)默认使用 ISO-8859-1 处理响应头。
3. **浏览器兼容性差异**
不同浏览器对 `filename` 和 `filename*` 参数的支持不一致。
---
#### 解决方案(Spring Boot 实现)
##### 方法 1:RFC 5987 标准编码(推荐)
```java
import org.springframework.http.ContentDisposition;
import org.springframework.http.HttpHeaders;
@GetMapping("/download-docx")
public ResponseEntity<Resource> downloadDocx() throws Exception {
// 1. 生成 Word 文档(示例)
File file = generateWordDocument(); // 你的文档生成逻辑
// 2. 设置 RFC 5987 编码的响应头
String fileName = "中文文件名.docx"; // 原始文件名
ContentDisposition disposition = ContentDisposition.builder("attachment")
.filename(fileName, StandardCharsets.UTF_8) // 关键:强制 UTF-8 编码
.build();
HttpHeaders headers = new HttpHeaders();
headers.setContentDisposition(disposition);
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
headers.add("Cache-Control", "no-cache, no-store, must-revalidate");
// 3. 返回文件流
Path path = Paths.get(file.getAbsolutePath());
ByteArrayResource resource = new ByteArrayResource(Files.readAllBytes(path));
return ResponseEntity.ok()
.headers(headers)
.contentLength(file.length())
.body(resource);
}
```
**优势**:
- 符合国际标准(RFC 5987),兼容现代浏览器(Chrome/Firefox/Edge)
- 通过 `filename*=UTF-8''` 语法明确指定编码
##### 方法 2:手动兼容旧浏览器
```java
String fileName = "中文文件名.docx";
String encodedFileName = URLEncoder.encode(fileName, "UTF-8").replace("+", "%20");
String headerValue = "attachment; " +
"filename=\"" + new String(fileName.getBytes("ISO-8859-1"), "ISO-8859-1") + "\"; " + // 旧浏览器兼容
"filename*=UTF-8''" + encodedFileName; // 新浏览器标准
response.setHeader("Content-Disposition", headerValue);
```
---
#### 关键配置补充
1. **强制 Servlet 容器编码**
在 `application.properties` 中添加:
```properties
server.servlet.encoding.force-response=true # 强制响应编码
server.servlet.encoding.charset=UTF-8
```
2. **容器字体支持(防内容乱码)**
若文档**内容**出现中文乱码,需确保容器有中文字体:
```dockerfile
# Dockerfile 示例
FROM openjdk:17
RUN apt-get update && apt-get install -y fonts-wqy-microhei # 安装文泉驿字体
COPY ./your-fonts /usr/share/fonts # 或拷贝自定义字体
```
3. **文件长度声明(防文件损坏)**
如引用 [^1] 所述,需显式设置内容长度:
```java
response.setContentLengthLong(file.length());
```
---
#### 技术原理
- **RFC 5987 标准**:
使用 `filename*=charset'lang'value` 语法(如 `filename*=UTF-8''%E4%B8%AD%E6%96%87.docx`)明确 UTF-8 编码。
- **浏览器兼容**:
- Chrome/Firefox:优先读取 `filename*`
- IE/旧版 Safari:回退到 `filename`
- **编码转换**:
`URLEncoder.encode()` 将中文字符转换为 `%xx` 形式(如 `中` → `%E4%B8%AD`)。
---
#### 测试验证
使用 Postman 或浏览器检查响应头:
```http
HTTP/1.1 200 OK
Content-Disposition: attachment; filename="??.docx"; filename*=UTF-8''%E4%B8%AD%E6%96%87.docx
Content-Type: application/octet-stream
```
确保第二个 `filename*` 参数包含 UTF-8 编码后的字符串。
阅读全文