个人博客: www.wdcdbd.com
java实现导出word文档是项目中很常见的操作,但又是很棘手的操作,这篇文章,带大家从0到1超详细的完整的操作一遍java实现导出word文档。一定要认真看这篇文章,超详细。话不多说直接上代码。
使用的依赖:
org.jsoup
jsoup
1.11.3
cn.hutool
hutool-all
5.8.16
org.apache.commons
commons-io
1.3.2
com.aspose
aspose-words
18.2
jdk16
com.aspose
aspose-pdf
18.2
com.aspose
aspose-cells
18.2
AsposeJavaAPI
Aspose Java API
https://repository.aspose.com/repo/
AsposeJavaAPI
https://repository.aspose.com/repo/
使用Java导出word文档,要记住三大核心,文档模版,文档和数据。
为什么这样说?因为使用Java导出word文档,其实是以html形式生成表格,列表,图片等信息。这点很重要。因为决定了你导出来word文档的样式,所以就需要上述提到的文档模版!!
先普及一下导出world文档的步骤:
我们先从数据库看起能够更好的理解为什么要这么做!
创建一个tpt_document_template(可自定义)表:包含三个重要字段id、 title、content
可以看到上面的数据库,这个content放的就是你的文档模版。一般都是在桌面上创建一个.html文件,设置好格式之后,粘贴到数据库中。如下:
文档模版展示:
文档模版样式代码:
文档模版完整代码:
测试标题
我是测试信息 测试文档编号
测试记录
测试编号${bh}
名称
${khmc}
学校描述:
对${scloolName}的描述。
个人签字
${grqz}
创建日期
${cjsj}
学校地点
${xxdd}
学校内容:
A.采集学生信息;
□
B.采集老师信息;□
P.采集家长信息:□
Q.采集其他信息;□
老师意见:
${lsyj}
看门大叔(签字): ${kmds}
学生意见:${xsyj}
宿管阿姨(签字): ${suay}
注:本次测试针对学习使用,无其他使用N/A,无不变使用。
文档模版一般是不会变更的,所以调试好格式之后,可以直接复制粘贴到你的数据库中的content中即可。
创建tptDocument表
实体类大家应该都会创建,这里就不多叙述了,我使用的是springBoot+mybatis-plus。给你们截个图看一下就行了,这个应该都会创建。
这里偷了一下懒,只展示了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 {
}
service层
public interface TptDocumentService extends IService {
}
@Service
public class TptDocumentServiceImpl extends ServiceImpl implements TptDocumentService {
}
util工具类
package com.atdession.util;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
public class Result {
//状态码
private int code;
//消息
private String message;
//返回的数据
private T data;
//集合
private List 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 listDate) {
this.code = code;
this.message = message;
this.data = data;
ListDate = listDate;
}
//成功没有数据
public static Result successNoDate(){
return new Result(HttpServletResponse.SC_OK,null,null);
}
//成功有数据
public static Result successDate(E date){
return new Result(HttpServletResponse.SC_OK,null,date);
}
//成功有数据
public static Result successListDate(List date){
return new Result(HttpServletResponse.SC_OK,null,null,date);
}
//失败提示及消息
public static Result failed(String message){
return new Result(HttpServletResponse.SC_GONE,message,null);
}
public List getListDate() {
return ListDate;
}
public void setListDate(List 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文档展示出来,让大家更直观的看到如何使用java导出word文档。了解内部原理,才能运筹帷幄。
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 getDocument(String title){
TptDocument tptDocument = tptDocumentService.getDocument(title);
tptDocumentService.saveOrUpdate(tptDocument);
return Result.successDate(tptDocument);
}
}
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 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().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;
}
}
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();
}
}
}
好了,都写好之后,启动项目。测试模拟一下吧!
然后去tpt_document表中 找到content字段,将里边的信息复制粘贴到.html文件中
复制进来之后,双击打开就会发现,你的数据已经设置进去了。
好的,有了对word这种深入的了解之后,相信你可以得心应手的修改各种样式和模版啦。
因为本次代码前端样式使用的事 Ant Design Vue 所以展示的时候我就是用这种组件了。你如果使用的是element-plus等js,就可以使用他们的组件。就是一个点击按钮,怎么来都可以。
下载
可以看到,此函数是先要保存文档,在下载,所以等下后台也按照这种步骤写。
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('请先保存文档!')
}
}
}
}
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);
}
}
对应前端的save()
controller层
@RequestMapping(value = "/save")
@Transactional(rollbackFor = Exception.class)
public Result 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 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 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().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.*;
/**
*
* 用户表 前端控制器
*
*
* @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);
}
}
好了,以上就是word的基本操作的全部内容啦,爆肝几个小时,呜呜呜,快去精进你的技术吧!