[含完整代码]Java实现导出word文档【超详细】
前言:
个人博客: www.wdcdbd.com
java实现导出word文档是项目中很常见的操作,但又是很棘手的操作,这篇文章,带大家从0到1超详细的完整的操作一遍java实现导出word文档。一定要认真看这篇文章,超详细。话不多说直接上代码。
〇、实现导出word文档前准备
使用的依赖:
<dependencies>
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.11.3</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.16</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-io</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>com.aspose</groupId>
<artifactId>aspose-words</artifactId>
<version>18.2</version>
<classifier>jdk16</classifier>
</dependency>
<dependency>
<groupId>com.aspose</groupId>
<artifactId>aspose-pdf</artifactId>
<version>18.2</version>
</dependency>
<dependency>
<groupId>com.aspose</groupId>
<artifactId>aspose-cells</artifactId>
<version>18.2</version>
</dependency>
</dependencies>
<repositories>
<repository>
<id>AsposeJavaAPI</id>
<name>Aspose Java API</name>
<url>https://repository.aspose.com/repo/</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>AsposeJavaAPI</id>
<url>https://repository.aspose.com/repo/</url>
</pluginRepository>
</pluginRepositories>
使用Java导出word文档,要记住三大核心,文档模版,文档和数据。
为什么这样说?因为使用Java导出word文档,其实是以html形式生成表格,列表,图片等信息。这点很重要。因为决定了你导出来word文档的样式,所以就需要上述提到的文档模版!!
先普及一下导出world文档的步骤:
- 保存数据 ----- 比如:名称,年龄,日期等信息
- 使用文档模版---- 文档模版很直观就是模版,只需要获取到你自己自定义的文档模版然后将你保存的数据替换进去或者更改里边的样式等信息。(可以理解为java的service层)主要就是操作数据的。
- 生成文档 ---- 而这个文档才是你要下载的word文档。
1.数据库
我们先从数据库看起能够更好的理解为什么要这么做!
创建一个tpt_document_template(可自定义)表:包含三个重要字段id、 title、content
可以看到上面的数据库,这个content放的就是你的文档模版。一般都是在桌面上创建一个.html文件,设置好格式之后,粘贴到数据库中。如下:
文档模版展示:
文档模版样式代码:
文档模版完整代码:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<meta http-equiv="Content-Style-Type" content="text/css"/>
<meta name="generator" content="Aspose.Words for Java 21.11.0"/>
<title>测试标题</title></head>
<body style="font-family:'Times New Roman'; font-size:10pt">
<div>
<div style="-aw-headerfooter-type:header-primary; clear:both"><p
style="margin-top:0pt; margin-bottom:0pt; text-align:justify; border-bottom:0.75pt solid #000000; padding-bottom:1pt; font-size:10.5pt">
<span style="font-family:宋体; font-weight:bold">我是测试信息</span><span
style="font-family:宋体; font-size:9pt; -aw-import:spaces">                </span><span
style="font-family:宋体; font-size:9pt">测试文档编号</span></p>
</div>
<p style="margin-top:0pt; margin-bottom:0pt; text-align:center; line-height:15pt"><span
style="font-family:宋体; font-size:15pt; font-weight:bold; -aw-import:ignore"> </span></p>
<p style="margin-top:0pt; margin-bottom:0pt; text-align:center; line-height:15pt">
<span
style="font-family:宋体; font-size:15pt; font-weight:bold">测试记录</span></p>
<p style="margin-top:0pt; margin-bottom:0pt; text-indent:326.7pt; line-height:10pt;margin-left: -40pt"><span style="font-family:宋体;">测试编号${bh}</span>
</p>
<table cellspacing="0" cellpadding="0"
style="-aw-border-insideh:0.75pt single #000000; -aw-border-insidev:0.75pt single #000000; border-collapse:collapse">
<tr style="height:22.6pt; page-break-inside:avoid">
<td
style="width:90pt; border-style:solid; border-width:3.75pt 0.75pt 0.75pt 3.75pt; padding-right:5.03pt; padding-left:3.53pt; vertical-align:middle; -aw-border-left:2.25pt thin-thick-small-gap; -aw-border-top:2.25pt thin-thick-small-gap">
<p style="margin-top:0pt; margin-bottom:0pt; text-align:center; line-height:10.5pt">
<span
style="font-family:宋体; font-weight:bold; font-size:10.5pt;">名称</span></p></td>
<td colspan="4"
style="width:374.9pt; border-style:solid; border-width:3.75pt 3.75pt 0.75pt 0.75pt; padding-right:3.53pt; padding-left:5.03pt; vertical-align:middle; -aw-border-right:2.25pt thick-thin-small-gap; -aw-border-top:2.25pt thin-thick-small-gap">
<!--<p style="margin-top:0pt; margin-bottom:0pt; font-size:14pt"><span style="font-family:宋体;font-size: 11.5pt;"><span</span></p>-->
<p style="margin-top:0pt; margin-bottom:0pt; line-height:10.5pt">
<span style="font-family:宋体; font-size:10.5pt;">${khmc}</span>
</p>
</td>
</tr>
<tr style="height:42.6pt">
<td colspan="5"
style="
width:475.7pt; border-style:solid; border-width:0.75pt 3.75pt; padding-right:3.53pt; padding-left:3.53pt; vertical-align:top; -aw-border-left:2.25pt thin-thick-small-gap; -aw-border-right:2.25pt thick-thin-small-gap
">
<p style="margin-top:0pt; margin-bottom:0pt;line-height:10.5pt">
<span style="font-family:宋体; font-weight:bold; font-size:10.5pt;">学校描述</span><span style="font-family:宋体; font-size:10.5pt;">:</span><br/>
<span style="font-family:宋体; font-size:10.5pt;">对${scloolName}的描述。</span><br/>
</p>
<p style="margin-top:0pt; margin-bottom:0pt; font-size:10pt"><span style="font-family:宋体; -aw-import:ignore"> </span>
</p>
<p style="margin-top:0pt; margin-bottom:0pt; font-size:10pt"><span style="font-family:宋体; -aw-import:ignore"> </span>
</p>
<p style="margin-top:0pt; margin-bottom:0pt; font-size:10pt"><span style="font-family:宋体; -aw-import:ignore"> </span>
</p></td>
</tr>
<tr style="height:19.6pt; page-break-inside:avoid">
<td
style="width:90pt; border-style:solid; border-width:0.75pt 0.75pt 0.75pt 3.75pt; padding-right:5.03pt; padding-left:3.53pt; vertical-align:middle; -aw-border-left:2.25pt thin-thick-small-gap">
<p style="margin-top:0pt; margin-bottom:0pt; text-align:center;line-height:10.5pt"><span style="font-family:宋体;font-weight:bold;font-size:10.5pt;">个人签字</span></p>
</td>
<td colspan="4"
style="width:374.9pt; border-style:solid; border-width:0.75pt 3.75pt 0.75pt 0.75pt; padding-right:3.53pt; padding-left:5.03pt; vertical-align:middle; -aw-border-right:2.25pt thick-thin-small-gap">
<p style="margin-top:0pt; margin-bottom:0pt"><span style="font-family:宋体; -aw-import:ignore"> </span></p>
<p style="margin-top:0pt; margin-bottom:0pt; line-height:10.5pt">
<span style="font-family:宋体; font-size:10.5pt;">${grqz}</span>
</p>
<p style="margin-top:0pt; margin-bottom:0pt"><span style="font-family:宋体; -aw-import:ignore"> </span></p>
</td>
</tr>
<tr style="height:20.95pt">
<td
style="width:90pt; border-style:solid; border-width:0.75pt 0.75pt 0.75pt 3.75pt; padding-right:5.03pt; padding-left:3.53pt; vertical-align:middle; -aw-border-left:2.25pt thin-thick-small-gap">
<p style="margin-top:0pt; margin-bottom:0pt; text-align:center;line-height:10.5pt"><span style="font-family:宋体;font-weight:bold; font-size:10.5pt;">创建日期</span></p>
</td>
<td colspan="2"
style="width:154.4pt; border-style:solid; border-width:0.75pt; padding-right:5.03pt; padding-left:5.03pt; vertical-align:middle">
<p style="margin-top:0pt; margin-bottom:0pt; line-height:10.5pt">
<span style="font-family:宋体; font-size:10.5pt;">${cjsj}</span>
</p>
</td>
<td
style="width:81.3pt; border-style:solid; border-width:0.75pt; padding-right:5.03pt; padding-left:5.03pt; vertical-align:middle">
<p style="margin-top:0pt; margin-bottom:0pt; text-align:center;line-height:10.5pt"><span style="font-family:宋体;font-weight:bold; font-size:10.5pt;">学校地点</span></p>
</td>
<td
style="width:117.6pt; border-style:solid; border-width:0.75pt 3.75pt 0.75pt 0.75pt; padding-right:3.53pt; padding-left:5.03pt; vertical-align:middle; -aw-border-right:2.25pt thick-thin-small-gap">
<p style="margin-top:0pt; margin-bottom:0pt; line-height:10.5pt">
<span style="font-family:宋体; font-size:10.5pt;">${xxdd}</span>
</p>
</td>
</tr>
<tr id="reviewContent" style="height:237.85pt; page-break-inside:avoid">
<td colspan="2"
style="width:232.45pt; border-style:solid; border-width:0.75pt 0.75pt 0.75pt 3.75pt; padding-right:5.03pt; padding-left:3.53pt; vertical-align:top; -aw-border-left:2.25pt thin-thick-small-gap">
<p style="margin-top:0pt; margin-bottom:0pt; line-height:10.5pt"><span style="font-family:宋体; font-weight:bold;font-size:10.5pt;">学校内容:</span>
</p>
<p style="margin-top:0pt; margin-bottom:0pt; line-height:10.5pt">
<span style="font-family:宋体;font-size:10.5pt;">A.采集学生信息;</span>
<span style="font-family:宋体; font-weight:bold;font-size:10.5pt;">□</span><span> </span>
</p>
<p style="margin-top:0pt; margin-left:15.9pt; margin-bottom:0pt; line-height:10.5pt"><span
style="font-family:宋体;font-size:10.5pt;">B.采集老师信息;</span><span
style="font-family:宋体; font-weight:bold;font-size:10.5pt;">□</span></p>
</td>
<td colspan="3"
style="width:232.45pt; border-style:solid; border-width:0.75pt 3.75pt 0.75pt 0.75pt; padding-right:3.53pt; padding-left:5.03pt; vertical-align:top; -aw-border-right:2.25pt thick-thin-small-gap">
<p style="margin-top:0pt; margin-bottom:0pt; line-height:10.5pt;"><span
style="font-family:宋体;font-size:10.5pt; ">P.采集家长信息:</span><span
style="font-family:宋体; font-weight:bold;font-size:10.5pt;">□</span></p>
<p style="margin-top:0pt; margin-bottom:0pt; line-height:10.5pt;"><span
style="font-family:宋体;font-size:10.5pt; ">Q.采集其他信息;</span><span
style="font-family:宋体; font-weight:bold;font-size:10.5pt;">□</span></p>
</tr>
<tr style="page-break-inside:avoid">
<td colspan="5"
style="width:475.7pt; border-style:solid; border-width:0.75pt 3.75pt; padding-right:3.53pt; padding-left:3.53pt; vertical-align:top; -aw-border-left:2.25pt thin-thick-small-gap; -aw-border-right:2.25pt thick-thin-small-gap">
<p style="margin-top:0pt; margin-bottom:0pt; line-height:10.5pt;">
<span style="font-family:宋体; font-size:10.5pt; font-weight:bold">老师意见:</span>
<span
style="font-family:宋体;font-size:10.5pt;">${lsyj}</span></p>
<p style="margin-top:0pt; margin-bottom:0pt; line-height:10pt"><span
style="font-family:宋体; font-size:10pt; -aw-import:ignore"> </span></p>
<p style="margin-top:0pt; margin-bottom:0pt; line-height:10.5pt"><span
style="font-family:宋体; font-size:10.5pt; -aw-import:ignore"> </span></p>
<p style="margin-top:0pt; margin-bottom:0pt; line-height:10.5pt"><span
style="font-family:宋体; font-size:10.5pt; -aw-import:ignore"> </span></p>
<p style="margin-top:0pt; margin-bottom:0pt; line-height:10.5pt"><span
style="font-family:宋体; font-size:10.5pt; -aw-import:ignore"> </span></p>
<p style="margin-top:0pt; margin-bottom:0pt; line-height:10.5pt"><span
style="font-family:宋体; font-size:10.5pt; -aw-import:ignore"> </span></p>
<p style="margin-top:0pt; margin-bottom:0pt; text-align:right; line-height:10.5pt"><span style="font-family:宋体; font-size:10.5pt; font-weight:bold">看门大叔(签字): ${kmds}</span><span
style="font-family:宋体; font-size:10.5pt; font-weight:bold; -aw-import:spaces">   </span><span
style="font-family:宋体; font-size:10.5pt; font-weight:bold; -aw-import:spaces"> </span>
<!--<span
style="font-family:宋体; font-size:10.5pt; font-weight:bold">${zcrqzrq}</span>-->
</p>
</td>
</tr>
<tr style="height:95.2pt; page-break-inside:avoid">
<td colspan="5"
style="width:475.7pt; border-style:solid; border-width:0.75pt 3.75pt 3.75pt; padding-right:3.53pt; padding-left:3.53pt; vertical-align:top; -aw-border-bottom:2.25pt thick-thin-small-gap; -aw-border-left:2.25pt thin-thick-small-gap; -aw-border-right:2.25pt thick-thin-small-gap">
<p style="margin-top:0pt; margin-bottom:0pt; line-height:10.5pt;">
<span style="font-family:宋体; font-size:10.5pt; font-weight:bold">学生意见:</span><span
style="font-family:宋体;font-size:10.5pt;">${xsyj}</span></p>
<p style="margin-top:0pt; margin-bottom:0pt; font-size:10.5pt"><span
style="font-family:宋体; font-weight:bold; -aw-import:ignore"> </span></p>
<p style="margin-top:0pt; margin-bottom:0pt; font-size:10pt"><span
style="font-family:宋体; font-weight:bold; -aw-import:ignore"> </span></p>
<p style="margin-top:0pt; margin-bottom:0pt; font-size:10pt"><span
style="font-family:宋体; font-weight:bold; -aw-import:ignore"> </span></p>
<p style="margin-top:0pt; margin-bottom:0pt; font-size:10pt"><span
style="font-family:宋体; font-weight:bold; -aw-import:ignore"> </span></p>
<p style="margin-top:0pt; margin-bottom:0pt; text-align:right; font-size:10.5pt"><span
style="font-family:宋体; font-weight:bold">宿管阿姨(签字): ${suay}</span><span
style="font-family:宋体; font-weight:bold; -aw-import:spaces">   </span><span
style="font-family:宋体; font-weight:bold; -aw-import:spaces"> </span>
<!--<span
style="font-family:宋体; font-weight:bold">${fzrqzrq}</span></p></td>-->
</tr>
<tr style="height:0pt">
<td style="width:100.8pt"></td>
<td style="width:142.45pt"></td>
<td style="width:22.75pt"></td>
<td style="width:92.1pt"></td>
<td style="width:128.4pt"></td>
</tr>
</table>
<p style="margin-top:0pt; margin-bottom:0pt"><span style="font-family:宋体">注:本次测试针对学习使用,无其他使用</span><span>N/A</span><span
style="font-family:宋体">,无不变使用。</span></p>
<p style="margin-top:0pt; margin-bottom:0pt; text-indent:375pt"><span style="-aw-import:ignore"> </span></p>
</div>
</body>
</html>
文档模版一般是不会变更的,所以调试好格式之后,可以直接复制粘贴到你的数据库中的content中即可。
创建tptDocument表
2、创建对应的实体类
实体类大家应该都会创建,这里就不多叙述了,我使用的是springBoot+mybatis-plus。给你们截个图看一下就行了,这个应该都会创建。
3、创建对应的controller、service、mapper层
这里偷了一下懒,只展示了tptDocument,你需要自己把tptDocumentTemplate的也创建出来
在创建一个student类。因为在数据库创建student表麻烦,所幸就直接创建student实体类,给他赋默认值。这个基础数据就不保存到数据库中了。
package com.atdession.entity;
import java.util.Date;
public class Student {
private String id;
private String name;
private String phone;
private String home;
private Date createDate;
private String classAddress;
public Student() {
}
public Student(String id, String name, String phone, String home, Date createDate, String classAddress) {
this.id = id;
this.name = name;
this.phone = phone;
this.home = home;
this.createDate = createDate;
this.classAddress = classAddress;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getHome() {
return home;
}
public void setHome(String home) {
this.home = home;
}
public Date getCreateDate() {
return createDate;
}
public void setCreateDate(Date createDate) {
this.createDate = createDate;
}
public String getClassAddress() {
return classAddress;
}
public void setClassAddress(String classAddress) {
this.classAddress = classAddress;
}
@Override
public String toString() {
return "Student{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
", phone='" + phone + '\'' +
", home='" + home + '\'' +
", createDate=" + createDate +
", classAddress='" + classAddress + '\'' +
'}';
}
}
mapper层
public interface TptDocumentMapper extends BaseMapper<TptDocument> {
}
service层
public interface TptDocumentService extends IService<TptDocument> {
}
@Service
public class TptDocumentServiceImpl extends ServiceImpl<TptDocumentMapper, TptDocument> implements TptDocumentService {
}
util工具类
package com.atdession.util;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
public class Result<T> {
//状态码
private int code;
//消息
private String message;
//返回的数据
private T data;
//集合
private List<T> ListDate;
public Result() {
}
public Result(int code, String message, T data) {
this.code = code;
this.message = message;
this.data = data;
}
public Result(int code, String message, T data, List<T> listDate) {
this.code = code;
this.message = message;
this.data = data;
ListDate = listDate;
}
//成功没有数据
public static <E>Result<E> successNoDate(){
return new Result<E>(HttpServletResponse.SC_OK,null,null);
}
//成功有数据
public static <E>Result<E> successDate(E date){
return new Result<E>(HttpServletResponse.SC_OK,null,date);
}
//成功有数据
public static <E>Result<E> successListDate(List<E> date){
return new Result<E>(HttpServletResponse.SC_OK,null,null,date);
}
//失败提示及消息
public static <E> Result<E> failed(String message){
return new Result<E>(HttpServletResponse.SC_GONE,message,null);
}
public List<T> getListDate() {
return ListDate;
}
public void setListDate(List<T> listDate) {
ListDate = listDate;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
@Override
public String toString() {
return "Result{" +
"code=" + code +
", message='" + message + '\'' +
", data=" + data +
", ListDate=" + ListDate +
'}';
}
}
controller层
@RestController
public class DocumentController {
//预览文档
@GetMapping("/getDocument")
public Result<?> getDocument(String title){
return null;
}
}
一、预览word文档
为了更方便大家了解,先写一个预览word文档展示出来,让大家更直观的看到如何使用java导出word文档。了解内部原理,才能运筹帷幄。
1、controller层
package com.atdession.controller;
import com.atdession.entity.TptDocument;
import com.atdession.service.TptDocumentService;
import com.atdession.util.Result;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class DocumentController {
@Autowired
TptDocumentService tptDocumentService;
//预览文档
@GetMapping("/getDocument")
public Result<TptDocument> getDocument(String title){
TptDocument tptDocument = tptDocumentService.getDocument(title);
tptDocumentService.saveOrUpdate(tptDocument);
return Result.successDate(tptDocument);
}
}
2、service层
package com.atdession.service.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.convert.Convert;
import com.atdession.entity.DocumentTemplate;
import com.atdession.entity.Student;
import com.atdession.entity.TptDocument;
import com.atdession.mapper.TptDocumentMapper;
import com.atdession.mapper.TptDocumentTemplateMapper;
import com.atdession.service.TptDocumentService;
import com.atdession.util.DocUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
@Service
public class TptDocumentServiceImpl extends ServiceImpl<TptDocumentMapper, TptDocument> implements TptDocumentService {
@Autowired
TptDocumentMapper tptDocumentMapper;
@Autowired
TptDocumentTemplateMapper tptDocumentTemplateMapper;
@Override
public TptDocument getDocument(String title) {
//创建文档类
TptDocument doc=new TptDocument();
// 通过 title获取模板信息
DocumentTemplate documentTemplate = this.tptDocumentTemplateMapper.selectOne(new LambdaQueryWrapper<DocumentTemplate>().eq(DocumentTemplate::getTitle, title));
//模版文件; 将content中的信息转换为document类型
Document document = Jsoup.parse(documentTemplate.getContent());
// 将转换后的文档转变为字符串设置进去
documentTemplate.setContent(String.valueOf(document));
//将模版信息copy到文档类中
BeanUtil.copyProperties(documentTemplate, doc);
/**
* 因为这是一个真实项目,所以该项目的流程是通过一个类查到基本信息,通过基本信息的id 查到 tptDocument。
* 基本信息的id就是fmainId
* 在这里面我们先随便设置一个更便于理解。
*/
doc.setFmainId("123456");
switch(title){
case "103学生信息表":
Student student = new Student("111","wdc","178374758","北京",new Date(),"北影");
doc = this.createDoc(doc, student, title);
break;
//如果你想写其他模版,可以比葫芦画瓢懂吧?
case "2其他模版":
break;
}
return doc;
}
private TptDocument createDoc(TptDocument doc, Student student, String title) {
//创建一个map,将student信息参照模版保存进去
Map map = new HashMap<>();
map.put("khmc",student.getName());
map.put("scloolName","北影");
//像这种value都一样的,完全可以使用同一个key,只需将模版中${}中的字母改成相同的就行,懒得改了,就这样写吧。哈哈
map.put("grqz",student.getName());
map.put("xxdd",student.getClassAddress());
//好,为了节省时间,目前先保存这么多,演示效果出来就行。
//返回处理后的数据
doc = this.changeReviewContent(doc,map);
//将map与文档中${}对应的信息进行替换。
DocUtil.replaceDocTextNew(doc,map);
return doc;
}
//修改文档样式
/**
* 说白了就是通过java操作html,这个很简单,通过debug看一遍就知道怎么回事了。
* @param doc
* @param map
* @return
*/
private TptDocument changeReviewContent(TptDocument doc, Map map) {
// 将String类型转换为Document类型
Document document = Jsoup.parse(doc.getContent());
//获取第一个标签为body的html
Element body = document.getElementsByTag("body").get(0);
//获取最后一个p标签
Element p = body.getElementsByTag("p").last();
p.getElementsByTag("span").get(0).text(Convert.toStr("小猪","默认值"));
//所以 你可以在这个方法中改变当前文档的任何值,任何样式,任何的任何!!。
//改好之后,设置回去并返回
doc.setContent(document.toString());
return doc;
}
}
3、工具类
package com.atdession.util;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.atdession.entity.TptDocument;
import java.awt.*;
import java.awt.Font;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class DocUtil {
// 经过Base64编码后的License
private static final String LICENSEBASE64 = "PExpY2Vuc2U+CiAgICA8RGF0YT4KICAgICAgICA8UHJvZHVjdHM+CiAgICAgICAgICAgIDxQcm9kdWN0PkFzcG9zZS5Ub3RhbCBmb3IgSmF2YTwvUHJvZHVjdD4KICAgICAgICAgICAgPFByb2R1Y3Q+QXNwb3NlLldvcmRzIGZvciBKYXZhPC9Qcm9kdWN0PgogICAgICAgIDwvUHJvZHVjdHM+CiAgICAgICAgPEVkaXRpb25UeXBlPkVudGVycHJpc2U8L0VkaXRpb25UeXBlPgogICAgICAgIDxTdWJzY3JpcHRpb25FeHBpcnk+MjA5OTEyMzE8L1N1YnNjcmlwdGlvbkV4cGlyeT4KICAgICAgICA8TGljZW5zZUV4cGlyeT4yMDk5MTIzMTwvTGljZW5zZUV4cGlyeT4KICAgICAgICA8U2VyaWFsTnVtYmVyPjhiZmUxOThjLTdmMGMtNGVmOC04ZmYwLWFjYzMyMzdiZjBkNzwvU2VyaWFsTnVtYmVyPgogICAgPC9EYXRhPgogICAgPFNpZ25hdHVyZT4KICAgICAgICBzTkxMS0dNVWRGMHI4TzFrS2lsV0FHZGdmczJCdkpiLzJYcDhwNWl1RFZmWlhtaHBwbytkMFJhbjFQOVRLZGpWNEFCd0FnS1h4SjNqY1FUcUUvMklSZnF3blBmOGl0TjhhRlpsVjNUSlBZZUQzeVdFN0lUNTVHejZFaWpVcEM3YUtlb29oVGI0dzJmcG94NTh3V29GM1NOcDZzSzZqRGZpQVVHRUhZSjlwalU9CiAgICA8L1NpZ25hdHVyZT4KPC9MaWNlbnNlPg==";
public static void replaceDocTextNew(TptDocument documentRoot, Map dataMap) {
if (documentRoot != null) {
String content = documentRoot.getContent();
// 需要替换的数据不能为空
if (StrUtil.isNotEmpty(content)) {
// 正则匹配
Pattern pattern = Pattern.compile("\\$\\{(.+?)}", Pattern.CASE_INSENSITIVE);
Matcher matcher = pattern.matcher(content);
while (matcher.find()) {
// 数据key
String dateKey = matcher.group(1);
// 数据替换
String targetValue = Convert.toStr(dataMap.get(dateKey), "");
// 只替换首次匹配的字符串
content = matcher.replaceFirst(targetValue);
// matcher指向新的字符串
matcher = pattern.matcher(content);
}
documentRoot.setContent(content);
}
}
}
/**
* Word转Pdf
*
* @author
* @date
*/
public static void html2Word(InputStream htmlInputStream, String docxPath) {
try {
File file = new File(docxPath);
if (file.exists()) {
file.delete();
}
getLicense();
Document document = new Document(htmlInputStream);
document.save(docxPath, SaveFormat.DOCX);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void getLicense() {
try {
InputStream inputStream = new ByteArrayInputStream(Base64.decode(LICENSEBASE64));
License license = new License();
license.setLicense(inputStream);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 添加页码
*
* @author
* @date 2022/07/19
*/
public static void addPageNo(String wordPath) {
try {
getLicense();
Document document = new Document(wordPath);
// 获取最后一节
Section lastSection = document.getLastSection();
// 添加页脚
HeaderFooter headerFooter = new HeaderFooter(document, HeaderFooterType.FOOTER_PRIMARY);
HeaderFooterCollection headersFooters = lastSection.getHeadersFooters();
headersFooters.add(headerFooter);
// 页脚段落
Paragraph paragraph = new Paragraph(document);
paragraph.getParagraphFormat().setAlignment(ParagraphAlignment.CENTER);
// 起始文本
Run beginRun = new Run(document);
beginRun.setText("第");
beginRun.getFont().setName("宋体");
// 小五号字体
beginRun.getFont().setSize(9.0);
paragraph.appendChild(beginRun);
// 添加页码
paragraph.appendField(FieldType.FIELD_PAGE, true);
// 中间文本
Run middleRun = new Run(document);
middleRun.setText("页 共");
middleRun.getFont().setName("宋体");
// 小五号字体
middleRun.getFont().setSize(9.0);
paragraph.appendChild(middleRun);
// 添加页码
paragraph.appendField(FieldType.FIELD_NUM_PAGES, true);
// 末尾文本
Run endRun = new Run(document);
endRun.setText("页");
endRun.getFont().setName("宋体");
// 小五号字体
endRun.getFont().setSize(9.0);
paragraph.appendChild(endRun);
// 页脚添加段落
headerFooter.appendChild(paragraph);
// 页面设置
PageSetup pageSetup = lastSection.getPageSetup();
// 重新编号
pageSetup.setRestartPageNumbering(true);
pageSetup.setPageStartingNumber(1);
// 更新域
document.updateFields();
document.save(wordPath);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 修改文档这里主要是修改标题序号的颜色样式
*
* @author lhl
* @date 2022/07/19
*/
public static void setStyle(String wordPath) {
try {
getLicense();
Document document = new Document(wordPath);
// 获取所有节
SectionCollection sections = document.getSections();
for (int i = 0; i < sections.getCount(); i++) {
Section section = sections.get(i);
// 获取没节的子节点
NodeCollection childNodes = section.getBody().getChildNodes();
for (int j = 0; j < childNodes.getCount(); j++) {
Node node = childNodes.get(j);
if (node instanceof Paragraph) {
// 获取段落
Paragraph paragraph = (Paragraph) node;
ListFormat listFormat = paragraph.getListFormat();
ListLevel listLevel = listFormat.getListLevel();
if (listLevel != null) {
// 设置字体颜色为黑色
listLevel.getFont().setColor(Color.BLACK);
}
}
}
}
document.save(wordPath);
} catch (Exception e) {
e.printStackTrace();
}
}
}
4、预览
好了,都写好之后,启动项目。测试模拟一下吧!
然后去tpt_document表中 找到content字段,将里边的信息复制粘贴到.html文件中
复制进来之后,双击打开就会发现,你的数据已经设置进去了。
好的,有了对word这种深入的了解之后,相信你可以得心应手的修改各种样式和模版啦。
二、下载word文档
1、前端代码
因为本次代码前端样式使用的事 Ant Design Vue 所以展示的时候我就是用这种组件了。你如果使用的是element-plus等js,就可以使用他们的组件。就是一个点击按钮,怎么来都可以。
<a-tooltip>
<template slot="title">
<span>下载</span>
</template>
<a-button :loading="loadDocing" @click="save(true)">
<a-icon type="download" />
</a-button>
</a-tooltip>
可以看到,此函数是先要保存文档,在下载,所以等下后台也按照这种步骤写。
data(){
methods: {
//函数方法
save(download) {
//自定义封装的ajax 参数一:路径,参数二:参数 这个参数你也可以自定义,在下述我只是用到了title,所以你可以值传一个title也行,适当的修改代码
putAction(this.url.save, this.docEntity)
.then((res) => {
if (res.success) {
this.$message.success('保存成功!')
this.docName = res.result
if (download) {
// 下载word文档
this.download()
}
} else {
this.$message.warning(res.message)
}
})
.finally(() => {
this.saving = false
this.loadDocing = false
//this.$refs.rich.showEtitor = false
})
},
// 下载文档
download() {
if (this.docName) {
// 下载文档 this.docName 这个参数表示下载的文档名称
// getFileAccessHttpUrl 封装的ajax 路径为 /sys/common/status/upload
let url = getFileAccessHttpUrl(this.docName)
window.open(url, '_blank')
} else {
this.$message.warning('请先保存文档!')
}
}
}
}
2、后端代码
2.1、工具类
public class oConvertUtils {
public static boolean isEmpty(Object object) {
if (object == null) {
return (true);
}
if ("".equals(object)) {
return (true);
}
if (CommonConstant.STRING_NULL.equals(object)) {
return (true);
}
return (false);
}
}
2.2、代码
对应前端的save()
controller层
@RequestMapping(value = "/save")
@Transactional(rollbackFor = Exception.class)
public Result<String> save(@RequestBody TptDocument tptDocument) {
String docName = tptDocumentService.saveDoc(tptDocument);
return Result.successDate(docName);
}
service层
package com.atdession.service;
import com.atdession.entity.TptDocument;
import com.baomidou.mybatisplus.extension.service.IService;
public interface TptDocumentService extends IService<TptDocument> {
TptDocument getDocument(String title);
String saveDoc(TptDocument tptDocument);
}
package com.atdession.service.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.IoUtil;
import com.atdession.entity.DocumentTemplate;
import com.atdession.entity.Student;
import com.atdession.entity.TptDocument;
import com.atdession.mapper.TptDocumentMapper;
import com.atdession.mapper.TptDocumentTemplateMapper;
import com.atdession.service.TptDocumentService;
import com.atdession.util.DocUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
@Service
public class TptDocumentServiceImpl extends ServiceImpl<TptDocumentMapper, TptDocument> implements TptDocumentService {
@Value("${jeecg.path.doc}")
private String upLoadPath;
@Autowired
TptDocumentMapper tptDocumentMapper;
@Autowired
TptDocumentTemplateMapper tptDocumentTemplateMapper;
@Override
public String saveDoc(TptDocument tptDocument) {
String docName = tptDocument.getTitle() + "-" + "自定义名称" + ".docx";
//Word文件路径
String docPath = upLoadPath + File.separator + docName;
// 获取文档根结点,查询条件[文档标题+被测件ID+pid]
// TptDocument documentRoot = this.getOne(new QueryWrapper<TptDocument>().lambda()
// .eq(TptDocument::getTitle, "生成报告").eq(TptDocument::getFmainId, tptDocument.getId()));
// 把合并的文档内容保存到Word和Pdf文件
InputStream wordInputStream = new ByteArrayInputStream(tptDocument.getContent().getBytes(StandardCharsets.UTF_8));
DocUtil.html2Word(wordInputStream, docPath);
IoUtil.close(wordInputStream);
// 添加文档页码
DocUtil.addPageNo(docPath);
// 修改文档样式
DocUtil.setStyle(docPath);
// 更新文档信息
this.updateById(tptDocument);
return docName;
}
}
对应前端的download()
package com.atdession.util;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
/**
* <p>
* 用户表 前端控制器
* </p>
*
* @Author scott
* @since 2018-12-20
*/
@RestController
@RequestMapping("/sys/common")
public class CommonController {
// 下载路径,自己去自定义
@Value(value = "${jeecg.path.doc}")
private String uploadpath;
/**
* 预览图片&下载文件
* 请求地址:http://localhost:8080/common/static/{user/20190119/e1fe9925bc315c60addea1b98eb1cb1349547719_1547866868179.jpg}
*
* @param request
* @param response
*/
@GetMapping(value = "/static/**")
public void view(HttpServletRequest request, HttpServletResponse response) {
// ISO-8859-1 ==> UTF-8 进行编码转换
String imgPath = extractPathFromPattern(request);
if(oConvertUtils.isEmpty(imgPath) || "null".equals(imgPath)){
return;
}
// 其余处理略
InputStream inputStream = null;
OutputStream outputStream = null;
try {
imgPath = imgPath.replace("..", "").replace("../","");
if (imgPath.endsWith(",")) {
imgPath = imgPath.substring(0, imgPath.length() - 1);
}
String filePath = uploadpath + File.separator + imgPath;
System.out.println("filePath = " + filePath);
File file = new File(filePath);
if(!file.exists()){
response.setStatus(404);
throw new RuntimeException("文件["+imgPath+"]不存在..");
}
// 设置强制下载不打开
response.setContentType("application/force-download");
response.addHeader("Content-Disposition", "attachment;fileName=" + new String(file.getName().getBytes("UTF-8"),"iso-8859-1"));
inputStream = new BufferedInputStream(new FileInputStream(filePath));
outputStream = response.getOutputStream();
byte[] buf = new byte[1024];
int len;
while ((len = inputStream.read(buf)) > 0) {
outputStream.write(buf, 0, len);
}
response.flushBuffer();
} catch (IOException e) {
// log.error("预览文件失败" + e.getMessage());
response.setStatus(404);
e.printStackTrace();
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
// log.error(e.getMessage(), e);
}
}
if (outputStream != null) {
try {
outputStream.close();
} catch (IOException e) {
// log.error(e.getMessage(), e);
}
}
}
}
/**
* 把指定URL后的字符串全部截断当成参数
* 这么做是为了防止URL中包含中文或者特殊字符(/等)时,匹配不了的问题
* @param request
* @return
*/
private static String extractPathFromPattern(final HttpServletRequest request) {
String path = (String) request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
String bestMatchPattern = (String) request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
return new AntPathMatcher().extractPathWithinPattern(bestMatchPattern, path);
}
}
3、展示
好了,以上就是word的基本操作的全部内容啦,爆肝几个小时,呜呜呜,快去精进你的技术吧!
更多推荐
所有评论(0)