SpringBoot動(dòng)態(tài)導(dǎo)出word文檔實(shí)整教程(復(fù)制即可使用)
背景
最近有一個(gè)需求是需要?jiǎng)討B(tài)導(dǎo)出合同、訂單等信息,導(dǎo)出一個(gè)word文檔供客戶進(jìn)行下載查看。
需要導(dǎo)出的word文件,主要可以分為兩種類型。
- 導(dǎo)出固定內(nèi)容和圖片的word文檔
- 導(dǎo)出表格內(nèi)容不固定的word文檔
經(jīng)過對(duì)比工具,我實(shí)踐過兩種實(shí)現(xiàn)方式。第一種是FreeMarker模板來進(jìn)行填充;第二種就是文中介紹的POI-TL。
這里我推薦使用POI-TL。
介紹
POI-TL是word模板引擎,基于Apache POI,提供更友好的API。
目前最新的版本是1.12.X,POI對(duì)應(yīng)版本是5.2.2。
這里需要注意的是POI和POI-TL有一個(gè)對(duì)應(yīng)的關(guān)系。

準(zhǔn)備工作
我使用的POI-TL版本是1.10.0

<dependency>
<groupId>com.deepoove</groupId>
<artifactId>poi-tl</artifactId>
<version>1.10.0</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>4.1.2</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>4.1.2</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml-schemas</artifactId>
<version>4.1.2</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.7</version>
</dependency>
快速開始
流程:制作模板->提供數(shù)據(jù)->渲染模板->下載word
注意:需要填充的數(shù)據(jù)需要使用{{}}來表示。
1. 導(dǎo)出固定內(nèi)容和圖片的word文檔
準(zhǔn)備模板

模板保存為docx格式,存放在resource目錄下

提供數(shù)據(jù)
private Map<String, Object> assertMap() {
Map<String, Object> params = new HashMap<>();
params.put("name", "努力的螞蟻");
params.put("age", "18");
params.put("image", Pictures.ofUrl("http://deepoove.com/images/icecream.png").size(100, 100).create());
return params;
}工具方法
/**
* 將項(xiàng)目中的模板文件拷貝到根目錄下
* @return
*/
private String copyTempFile(String templeFilePath) {
InputStream inputStream = getClass().getClassLoader().getResourceAsStream(templeFilePath);
String tempFileName = System.getProperty("user.home") + "/" + "1.docx";
File tempFile = new File(tempFileName);
try {
FileUtils.copyInputStreamToFile(inputStream, tempFile);
} catch (IOException e) {
throw new RuntimeException(e);
}
return tempFile.getPath();
}private void down(HttpServletResponse response, String filePath, String realFileName) {
String percentEncodedFileName = null;
try {
percentEncodedFileName = percentEncode(realFileName);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
StringBuilder contentDispositionValue = new StringBuilder();
contentDispositionValue.append("attachment; filename=").append(percentEncodedFileName).append(";").append("filename*=").append("utf-8''").append(percentEncodedFileName);
response.addHeader("Access-Control-Allow-Origin", "*");
response.addHeader("Access-Control-Expose-Headers", "Content-Disposition,download-filename");
response.setHeader("Content-disposition", contentDispositionValue.toString());
response.setHeader("download-filename", percentEncodedFileName);
try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(filePath));
// 輸出流
BufferedOutputStream bos = new BufferedOutputStream(response.getOutputStream());) {
byte[] buff = new byte[1024];
int len = 0;
while ((len = bis.read(buff)) > 0) {
bos.write(buff, 0, len);
}
} catch (Exception e) {
e.printStackTrace();
}
}/**
* 百分號(hào)編碼工具方法
* @param s 需要百分號(hào)編碼的字符串
* @return 百分號(hào)編碼后的字符串
*/
public static String percentEncode(String s) throws UnsupportedEncodingException {
String encode = URLEncoder.encode(s, StandardCharsets.UTF_8.toString());
return encode.replaceAll("\\+", "%20");
}編寫接口
@RequestMapping("genera")
public void genera(HttpServletResponse response) {
//1.組裝數(shù)據(jù)
Map<String, Object> params = assertMap();
//2.獲取根目錄,創(chuàng)建模板文件
String path = copyTempFile("word/1.docx");
String fileName = System.currentTimeMillis() + ".docx";
String tmpPath = "D:\\" + fileName;
try {
//3.將模板文件寫入到根目錄
//4.編譯模板,渲染數(shù)據(jù)
XWPFTemplate template = XWPFTemplate.compile(path).render(params);
//5.寫入到指定目錄位置
FileOutputStream fos = new FileOutputStream(tmpPath);
template.write(fos);
fos.flush();
fos.close();
template.close();
//6.提供前端下載
down(response, tmpPath, fileName);
} catch (Exception e) {
e.printStackTrace();
} finally {
//7.刪除臨時(shí)文件
File file = new File(tmpPath);
file.delete();
File copyFile = new File(path);
copyFile.delete();
}
}對(duì)于圖片的格式,POI-TL也提供了幾種方式來提供支撐。

測(cè)試
請(qǐng)求接口:http://127.0.0.1:1000/file/genera
效果如下:

2. 導(dǎo)出表格內(nèi)容不固定的word文檔
表格動(dòng)態(tài)內(nèi)容填充,POI-TL提供了3種方式。
- 表格行循環(huán)
- 表格列循環(huán)
- 動(dòng)態(tài)表格。
第二種和第三種都可以實(shí)現(xiàn)表格填充,但我個(gè)人感覺第一種更方便一點(diǎn),這里我只介紹【表格行循環(huán)】實(shí)現(xiàn)方式。
LoopRowTableRenderPolicy 是一個(gè)特定場(chǎng)景的插件,根據(jù)集合數(shù)據(jù)循環(huán)表格行。
注意:
- 模板中有兩個(gè)list,這兩個(gè)list需要置于循環(huán)行的上一行。
- 循環(huán)行設(shè)置要循環(huán)的標(biāo)簽和內(nèi)容,注意此時(shí)的標(biāo)簽應(yīng)該使用[]
準(zhǔn)備模板

提供數(shù)據(jù)
學(xué)生實(shí)體類
public class Student {
private String name;
private String age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
}
學(xué)生word類
public class StudentTable {
private String title;
private List<Student> studentList;
private List<Student> studentList1;
public List<Student> getStudentList1() {
return studentList1;
}
public void setStudentList1(List<Student> studentList1) {
this.studentList1 = studentList1;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public List<Student> getStudentList() {
return studentList;
}
public void setStudentList(List<Student> studentList) {
this.studentList = studentList;
}
}
表格數(shù)據(jù)
private StudentTable assertData() {
StudentTable table = new StudentTable();
table.setTitle("我是標(biāo)題");
List<Student> studentList = new ArrayList<>();
Student student = new Student();
student.setName("張三");
student.setAge("18");
studentList.add(student);
Student student1 = new Student();
student1.setName("李四");
student1.setAge("20");
studentList.add(student1);
Student student2 = new Student();
student2.setName("王五");
student2.setAge("21");
studentList.add(student2);
Student student3 = new Student();
student3.setName("馬六");
student3.setAge("19");
studentList.add(student3);
table.setStudentList(studentList);
table.setStudentList1(studentList);
return table;
}編寫接口
@RequestMapping("dynamicTable")
public void dynamicTable(HttpServletResponse response) {
//1.組裝數(shù)據(jù)
StudentTable table = assertData();
//2.獲取根目錄,創(chuàng)建模板文件
String path = copyTempFile("word/2.docx");
//3.獲取臨時(shí)文件
String fileName = System.currentTimeMillis() + ".docx";
String tmpPath = "D:\\" + fileName;
try {
//4.編譯模板,渲染數(shù)據(jù)
LoopRowTableRenderPolicy hackLoopTableRenderPolicy = new LoopRowTableRenderPolicy();
Configure config =
Configure.builder().bind("studentList", hackLoopTableRenderPolicy).bind("studentList1", hackLoopTableRenderPolicy).build();
XWPFTemplate template = XWPFTemplate.compile(path, config).render(table);
//5.寫入到指定目錄位置
FileOutputStream fos = new FileOutputStream(tmpPath);
template.write(fos);
fos.flush();
fos.close();
template.close();
//6.提供下載
down(response, tmpPath, fileName);
} catch (Exception e) {
e.printStackTrace();
} finally {
//7.刪除臨時(shí)文件
File file = new File(tmpPath);
file.delete();
File copyFile = new File(path);
copyFile.delete();
}
}測(cè)試
請(qǐng)求接口:http://127.0.0.1:1000/file/dynamicTable
效果如下:

總結(jié)
到此這篇關(guān)于SpringBoot動(dòng)態(tài)導(dǎo)出word文檔實(shí)整教程的文章就介紹到這了,更多相關(guān)SpringBoot動(dòng)態(tài)導(dǎo)出word文檔內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java實(shí)現(xiàn)對(duì)一行英文進(jìn)行單詞提取功能示例
這篇文章主要介紹了Java實(shí)現(xiàn)對(duì)一行英文進(jìn)行單詞提取功能,結(jié)合實(shí)例形式分析了java基于StringTokenizer類進(jìn)行字符串分割的相關(guān)操作技巧,需要的朋友可以參考下2017-10-10
java并發(fā)編程專題(九)----(JUC)淺析CyclicBarrier
這篇文章主要介紹了java CyclicBarrier的相關(guān)資料,文中示例代碼非常詳細(xì),幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下2020-07-07
一文帶你深入了解Java中延時(shí)任務(wù)的實(shí)現(xiàn)
延時(shí)任務(wù)相信大家都不陌生,在現(xiàn)實(shí)的業(yè)務(wù)中應(yīng)用場(chǎng)景可以說是比比皆是。這篇文章主要為大家介紹幾種實(shí)現(xiàn)延時(shí)任務(wù)的辦法,感興趣的可以了解一下2022-11-11
詳解Spring Security的formLogin登錄認(rèn)證模式
對(duì)于一個(gè)完整的應(yīng)用系統(tǒng),與登錄驗(yàn)證相關(guān)的頁面都是高度定制化的,非常美觀而且提供多種登錄方式。這就需要Spring Security支持我們自己定制登錄頁面,也就是本文給大家介紹的formLogin模式登錄認(rèn)證模式,感興趣的朋友跟隨小編一起看看吧2019-11-11

