1.前言
由于最近做了一个需求,界面上有一个按钮,点击按钮后将一个文件夹中的word文档显示在页面中,并且有一个下拉框可以选择不同的文档,选择文档可以显示该文档。这里我选择使用fr.opensagres.poi.xwpf.converter.pdf-gae
依赖包来实现。
2.依赖
这里我只依赖了这些依赖包
dependency>
groupId>fr.opensagres.xdocreport/groupId>
artifactId>fr.opensagres.poi.xwpf.converter.pdf-gae/artifactId>
version>2.0.1/version>
/dependency>
!-- Apache PDFBox 依赖用于.docx转PDF -->
dependency>
groupId>org.apache.pdfbox/groupId>
artifactId>pdfbox/artifactId>
version>3.0.0/version> !-- 根据最新版本调整 -->
/dependency>
3.代码
Java代码部分,这里我使用了两个文件夹中的文档
package com.hxgis.controller;
import java.io.File;
import java.io.FileInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import fr.opensagres.poi.xwpf.converter.pdf.PdfConverter;
import fr.opensagres.poi.xwpf.converter.pdf.PdfOptions;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource;
import org.springframework.http.ResponseEntity;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
//import org.apache.poi.xwpf.converter.pdf.PdfOptions;
//import org.apache.poi.xwpf.converter.pdf.PdfConverter;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
* ClassName: DocumentController
* Package: com.hxgis.controller
* Description:
*
* @Author dhn
* @Create 2024/1/31 11:56
* @Version 1.0
*/
@RestController
@RequestMapping("/api/document")
public class DocumentController {
private final String MONTH_PATH = "D:/ftp_data/month"; // 修改为你的文档存储路径
private final String QUARTER_PATH = "D:/ftp_data/quarter"; // 修改为你的文档存储路径
// private final String MONTH_PATH = "/home/geli/hbnyWeatherReport/month"; // 修改为你的文档存储路径
// private final String QUARTER_PATH = "/home/geli/hbnyWeatherReport/quarter"; // 修改为你的文档存储路径
@GetMapping("/monthLatest")
public ResponseEntityResource> getDocument(@RequestParam(required = false) String name) throws IOException {
File folder = new File(MONTH_PATH);
File[] files = folder.listFiles((dir, filename) -> filename.endsWith(".docx"));
if (files == null || files.length == 0) {
return ResponseEntity.notFound().build();
}
File fileToConvert;
if (name != null && !name.isEmpty()) {
// 用户选择了特定的文件
fileToConvert = Arrays.stream(files)
.filter(file -> file.getName().equals(name))
.findFirst()
.orElse(null);
} else {
// 没有特定选择,找最新的文件
Pattern pattern = Pattern.compile("(\d{4})年(\d{2})月风光资源趋势预测\.docx");
fileToConvert = Arrays.stream(files)
.filter(file -> pattern.matcher(file.getName()).matches())
.max(Comparator.comparingInt(file -> {
Matcher matcher = pattern.matcher(file.getName());
if (matcher.find()) {
int year = Integer.parseInt(matcher.group(1));
int month = Integer.parseInt(matcher.group(2));
return year * 100 + month;
}
return 0;
}))
.orElse(null);
}
if (fileToConvert == null) {
return ResponseEntity.notFound().build();
}
// 以下为文件转换为PDF的代码
try (XWPFDocument document = new XWPFDocument(new FileInputStream(fileToConvert));
ByteArrayOutputStream out = new ByteArrayOutputStream()) {
PdfOptions options = PdfOptions.create();
PdfConverter.getInstance().convert(document, out, options);
byte[] pdfContent = out.toByteArray();
ByteArrayResource resource = new ByteArrayResource(pdfContent);
return ResponseEntity.ok()
.contentLength(pdfContent.length)
.header("Content-type", "application/pdf")
.body(resource);
}
}
@GetMapping("/quarterLatest")
public ResponseEntityResource> quarterLatest(@RequestParam(required = false) String name) throws IOException {
File folder = new File(QUARTER_PATH);
File[] files = folder.listFiles((dir, filename) -> filename.endsWith(".docx"));
if (files == null || files.length == 0) {
return ResponseEntity.notFound().build();
}
File fileToConvert;
if (name != null && !name.isEmpty()) {
// 用户选择了特定的文件
fileToConvert = Arrays.stream(files)
.filter(file -> file.getName().equals(name))
.findFirst()
.orElse(null);
} else {
// 没有特定选择,找最新的文件
Pattern pattern = Pattern.compile("(\d{4})年(?:\d{2})月-(\d{4})年(?:\d{2})月风光资源趋势预测\.docx|" +
"(\d{4})年(?:\d{2})-(\d{2})月风光资源趋势预测\.docx");
fileToConvert = Arrays.stream(files)
.filter(file -> pattern.matcher(file.getName()).matches())
.max(Comparator.comparingInt(file -> {
Matcher matcher = pattern.matcher(file.getName());
if (matcher.find()) {
if (matcher.group(1) != null) {
// 处理跨年文件名
int yearStart = Integer.parseInt(matcher.group(1));
int yearEnd = Integer.parseInt(matcher.group(2));
return yearEnd * 100 + (matcher.group(4) != null ? Integer.parseInt(matcher.group(4)) : 0);
} else if (matcher.group(3) != null) {
// 处理同一年文件名
int year = Integer.parseInt(matcher.group(3));
int monthEnd = Integer.parseInt(matcher.group(4));
return year * 100 + monthEnd;
}
}
return 0;
}))
.orElse(null);
}
if (fileToConvert == null) {
return ResponseEntity.notFound().build();
}
// 文件转换为PDF的代码
try (XWPFDocument document = new XWPFDocument(new FileInputStream(fileToConvert));
ByteArrayOutputStream out = new ByteArrayOutputStream()) {
PdfOptions options = PdfOptions.create();
PdfConverter.getInstance().convert(document, out, options);
byte[] pdfContent = out.toByteArray();
ByteArrayResource resource = new ByteArrayResource(pdfContent);
return ResponseEntity.ok()
.contentLength(pdfContent.length)
.header("Content-type", "application/pdf")
.body(resource);
}
}
@GetMapping("/monthList")
public ListString> listAllDocs() {
File folder = new File(MONTH_PATH);
// 修改为获取.docx文件
File[] files = folder.listFiles((dir, name) -> name.endsWith(".docx"));
if (files == null) {
return Collections.emptyList();
}
// 返回文件名列表
return Arrays.stream(files)
.map(File::getName)
.collect(Collectors.toList());
}
@GetMapping("/quarterList")
public ListString> quarterList() {
File folder = new File(QUARTER_PATH);
// 修改为获取.docx文件
File[] files = folder.listFiles((dir, name) -> name.endsWith(".docx"));
if (files == null) {
return Collections.emptyList();
}
// 返回文件名列表
return Arrays.stream(files)
.map(File::getName)
.collect(Collectors.toList());
}
}
前端代码html代码,使用两个按钮,点击后弹出模态框,在模态框中有iframe来展示pdf:
div class="modal fade" id="pdfModalMonth" tabindex="-1" role="dialog" aria-labelledby="pdfModalLabelMonth" aria-hidden="true">
div class="modal-dialog" style="max-width: 90%; width: auto;">
div class="modal-content">
div class="modal-header">
h4 class="modal-title" id="pdfModalLabelMonth">月度预测文档h4>
button type="button" class="close" data-dismiss="modal" aria-label="Close">
span aria-hidden="true">×span>
button>
div>
div class="modal-body">
select id="MonthList" class="form-control">
option value="">请选择文档option>
select>
iframe id="MonthViewer" style="width:100%; height:700px;">iframe>
div>
div class="modal-footer">
button type="button" class="btn btn-default" data-dismiss="modal">关闭button>
div>
div>
div>
div>
div class="modal fade" id="pdfModalQuarter" tabindex="-1" role="dialog" aria-labelledby="pdfModalLabelQuarter" aria-hidden="true">
div class="modal-dialog" style="max-width: 90%; width: auto;">
div class="modal-content">
div class="modal-header">
h4 class="modal-title" id="pdfModalLabelQuarter">月度预测文档h4>
button type="button" class="close" data-dismiss="modal" aria-label="Close">
span aria-hidden="true">×span>
button>
div>
div class="modal-body">
select id="QuarterList" class="form-control">
option value="">请选择文档option>
select>
iframe id="QuarterViewer" style="width:100%; height:700px;">iframe>
div>
div class="modal-footer">
button type="button" class="btn btn-default" data-dismiss="modal">关闭button>
div>
div>
div>
div>
a class="btn btn-sm btn-primary" id="loadMonth" style="text-decoration: none;color: #ffffff">月度预测a>
a class="btn btn-sm btn-primary" id="loadQuarter" style="text-decoration: none;color: #ffffff">季度预测a>
js代码:
$(document).ready(function() {
// 加载月度列表
function loadMonthList() {
$.get("/api/document/monthList", function(data) {
// 清空现有的选项
$("#MonthList").empty();
$("#MonthList").append('请选择文档');
// 假设文档名包含日期,例如 "2024年01月风光资源趋势预测.docx"
// 对文档进行倒序排序
data.sort(function(a, b) {
// 转换为日期格式进行比较
var dateA = new Date(a.split('年')[0], a.split('年')[1].split('月')[0] - 1);
var dateB = new Date(b.split('年')[0], b.split('年')[1].split('月')[0] - 1);
return dateB - dateA; // 从新到旧排序
});
// 添加新的选项
data.forEach(function(doc) {
$("#MonthList").append('+ doc + '">' + doc + '');
});
});
}
// 当点击加载最新文档的按钮时
$("#loadMonth").click(function() {
loadMonthList(); // 调用函数加载文档列表
// 加载最新的PDF文档
$("#MonthViewer").attr("src", "/api/document/monthLatest");
// 显示模态框
$("#pdfModalMonth").modal("show");
});
// 当选择不同的文档时
$("#MonthList").change(function() {
var selectedFile = $(this).val();
if(selectedFile) {
$("#MonthViewer").attr("src", "/api/document/monthLatest?name=" + encodeURIComponent(selectedFile));
}
});
// 加载季度列表
function parseDateFromDocName(docName) {
var year, month;
var parts = docName.match(/(d{4})年(d{2})-(d{2})月|(d{4})年(d{2})月-(d{4})年(d{2})月/);
if (parts) {
if (parts[1]) {
// 格式是 "YYYY年MM-DD月"
year = parseInt(parts[1], 10);
month = parseInt(parts[2], 10); // 使用开始月份
} else {
// 格式是 "YYYY年MM月-YYYY年MM月"
year = parseInt(parts[4], 10);
month = parseInt(parts[5], 10); // 使用开始月份
}
}
return new Date(year, month - 1); // JavaScript中的月份是从0开始的
}
function loadQuarterList() {
$.get("/api/document/quarterList", function(data) {
// 清空现有的选项
$("#QuarterList").empty();
$("#QuarterList").append('请选择文档');
// 对文档进行倒序排序
data.sort(function(a, b) {
var dateA = parseDateFromDocName(a);
var dateB = parseDateFromDocName(b);
return dateB - dateA; // 从新到旧排序
});
// 添加新的选项
data.forEach(function(doc) {
$("#QuarterList").append('+ doc + '">' + doc + '');
});
});
}
// 当点击加载最新文档的按钮时
$("#loadQuarter").click(function() {
loadQuarterList(); // 调用函数加载文档列表
// 加载最新的PDF文档
$("#QuarterViewer").attr("src", "/api/document/quarterLatest");
// 显示模态框
$("#pdfModalQuarter").modal("show");
});
// 当选择不同的文档时
$("#QuarterList").change(function() {
var selectedFile = $(this).val();
if(selectedFile) {
$("#QuarterViewer").attr("src", "/api/document/quarterLatest?name=" + encodeURIComponent(selectedFile));
}
});
});
4.遇到的问题
做完一切后,发现有些文档中的标题的中文没有显示出来,我就对比显示的文档和没显示的文档,发现是因为字体的原因,宋体是可以显示出来的,但是宋体(中文)显示不出来。把宋体(中文)改成宋体就可以显示。
但是这只是我windows系统上运行是没问题的,我把项目部署到服务器(centos)后,发现中文一点都展示不出来,这时候我就很纳闷,为什么在windows上能显示出来,linux上显示不出来,经过查阅资料,我发现是由于Linux上缺乏一些中文字体,例如宋体、仿宋等,这些字体是我文档中用到的字体,所以下一步我要将windows中的字体放在服务器上。
5.在Linux中安装宋体
在Linux系统中安装宋体(SimSun)字体,需要手动下载字体文件或从Windows系统中复制字体文件,然后将其安装到Linux系统中。宋体不包含在开源字体包中,因为它是微软的商业字体。下面是一般步骤:
- 从Windows复制:如果你有访问Windows系统的权限,可以从C:WindowsFonts目录找到simsun.ttc(宋体)和其他中文字体文件,并将其复制到你的Linux系统中。
- 在Linux系统上安装字体一般有以下几个步骤:
- 创建字体目录(如果尚不存在)
sudo mkdir -p /usr/share/fonts/chinese
- 将字体文件复制到创建的目录中,假设你已经将simsun.ttc字体文件复制到了Linux系统的某个位置(例如~/Downloads),运行以下命令将其移动到字体目录:
sudo cp ~/Downloads/simsun.ttc /usr/share/fonts/chinese/
- 更新字体缓存,安装完字体后,需要更新字体缓存,以便系统识别新安装的字体:
sudo fc-cache -fv
- 安装后,你可以使用fc-list命令确认字体是否已正确安装:
fc-list | grep "simsun"
至此,大功告成!