1、解决富文本导入导出依赖兼容问题
2、处理富文本和非富文本内容
3、解决webp格式通过java下载不了问题,如果要用到富文本导出,将来势必是会碰到的bug,这里提前给提出来并解决,测试用例中有给图片测试。
4、在原有方法上优化,比如处理等比缩小图片、将图片本地路径,替换为minio或者base64格式
gitee测试用例:
链接: https://gitee.com/muyangrenOvo/word-import-export
注意:与文章代码有出入,但思路是一样的。只是获取文件的方式变了,一个是前端调用组件传的,一个是自己new file。
org.apache.poi poi4.1.2 org.apache.poi poi-ooxml-schemas4.1.2 org.apache.poi poi-ooxml4.1.2 fr.opensagres.xdocreport xdocreport2.0.2 org.apache.poi poi-scratchpad4.1.2 io.github.draco1023 poi-tl-ext0.4.2 org.jsoup jsoup1.15.3 com.github.nintha webp-imageio-core0.1.0
例如这是word文档,我们要通过波浪线去截取对应内容

@ApiLog("导入模板")
@PostMapping("/importTemplate")
@ApiOperation(value = "导入模板", notes = "传file")
public R importCase(@RequestParam MultipartFile file) {
return R.data(caseInfoService.importTemplate(file));
}
import com.deepoove.poi.XWPFTemplate;
import com.deepoove.poi.config.Configure;
import com.deepoove.poi.config.ConfigureBuilder;
import fr.opensagres.poi.xwpf.converter.core.FileImageExtractor;
import fr.opensagres.poi.xwpf.converter.core.FileURIResolver;
import fr.opensagres.poi.xwpf.converter.core.XWPFConverterException;
import fr.opensagres.poi.xwpf.converter.xhtml.XHTMLConverter;
import fr.opensagres.poi.xwpf.converter.xhtml.XHTMLOptions;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.ddr.poi.html.HtmlRenderPolicy;
@Override
public CaseInfoVO importTemplate(MultipartFile file){
try {
caseInfoVO = new CaseInfoVO();
//1、处理非富文本内容基本信息(讲解的是富文本导入,所以该内容略过)
//List
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author: muyangren
* @Date: 2022/12/14
* @Description:
* @Version: 1.0
*/
public class HtmlUtil {
/**
* 通过正则表达式去获取html中的src
*
* @param content
* @return
*/
public static List regexMatchPicture(String content) {
//用来存储获取到的图片地址
List srcList = new ArrayList<>();
//匹配字符串中的img标签
Pattern p = Pattern.compile("<(img|IMG)(.*?)(>|>|/>)");
Matcher matcher = p.matcher(content);
boolean hasPic = matcher.find();
//判断是否含有图片
if (hasPic) {
//如果含有图片,那么持续进行查找,直到匹配不到
while (hasPic) {
//获取第二个分组的内容,也就是 (.*?)匹配到的
String group = matcher.group(2);
//匹配图片的地址
Pattern srcText = Pattern.compile("(src|SRC)=(\"|\')(.*?)(\"|\')");
Matcher matcher2 = srcText.matcher(group);
if (matcher2.find()) {
//把获取到的图片地址添加到列表中
srcList.add(matcher2.group(3));
}
//判断是否还有img标签
hasPic = matcher.find();
}
}
return srcList;
}
/**
* 通过正则表达式去获取html中的src中的宽高
*
* @param content
* @return
*/
public static List> regexMatchWidthAndHeight(String content) {
//用来存储获取到的图片地址
List> srcList = new ArrayList<>();
//匹配字符串中的img标签
Pattern p = Pattern.compile("<(img|IMG)(.*?)(>|>|/>)");
//匹配字符串中的style标签中的宽高
String regexWidth = "width:(?\\d+([.]\\d+)?)(px|pt)";
String regexHeight = "height:(?\\d+([.]\\d+)?)(px;|pt;)";
Matcher matcher = p.matcher(content);
boolean hasPic = matcher.find();
//判断是否含有图片
if (hasPic) {
//如果含有图片,那么持续进行查找,直到匹配不到
while (hasPic) {
HashMap hashMap = new HashMap<>();
//获取第二个分组的内容,也就是 (.*?)匹配到的
String group = matcher.group(2);
hashMap.put("fileUrl", group);
//匹配图片的地址
Pattern srcText = Pattern.compile(regexWidth);
Matcher matcher2 = srcText.matcher(group);
String imgWidth = null;
String imgHeight = null;
if (matcher2.find()) {
imgWidth = matcher2.group("width");
}
srcText = Pattern.compile(regexHeight);
matcher2 = srcText.matcher(group);
if (matcher2.find()) {
imgHeight = matcher2.group("height");
}
hashMap.put("width", imgWidth);
hashMap.put("height", imgHeight);
srcList.add(hashMap);
//判断是否还有img标签
hasPic = matcher.find();
}
for (HashMap imagesFile : srcList) {
String height = imagesFile.get("height");
String width = imagesFile.get("width");
String fileUrl = imagesFile.get("fileUrl");
//1厘米=25px(像素) 17厘米(650px) word最大宽值
if (Func.isNotEmpty(width)) {
BigDecimal widthDecimal = new BigDecimal(width);
BigDecimal maxWidthWord = new BigDecimal("650.0");
if (widthDecimal.compareTo(maxWidthWord) > 0) {
BigDecimal divide = widthDecimal.divide(maxWidthWord, 2, RoundingMode.HALF_UP);
fileUrl = fileUrl.replace("width:" + width, "width:" + maxWidthWord);
if (Func.isNotEmpty(height)) {
BigDecimal heightDecimal = new BigDecimal(height);
BigDecimal divide1 = heightDecimal.divide(divide, 1, RoundingMode.HALF_UP);
fileUrl = fileUrl.replace("height:" + height, "height:" + divide1);
} else {
fileUrl = fileUrl.replace("height:auto", "height:350px");
}
imagesFile.put("newFileUrl", fileUrl);
} else {
imagesFile.put("newFileUrl", "");
}
}
}
}
return srcList;
}
}
参考文献
链接: https://github.com/draco1023/poi-tl-ext
模板如图所示

@ApiLog("模板-下载")
@GetMapping("/downloadTemplate")
@ApiOperation(value = "模板-下载")
public void downloadCaseInfo(HttpServletResponse response,CaseInfoDTO caseInfoDTO) {
caseInfoService.downloadTemplate(response,caseInfoDTO);
}
@Override
public void downloadTemplate(HttpServletResponse response, CaseInfoDTO caseInfoDTO) {
try {
//查询需要导入的数据
List caseInfoVOS = baseMapper.caseQueryPage(null, null, caseInfoDTO, AuthUtil.getUserId());
CaseInfoVO caseInfoVO = caseInfoVOS.get(0);
//处理作者名称
dealWithCaseAuthorName(caseInfoVOS);
Integer formatType = caseInfoVO.getFormatType();
org.springframework.core.io.Resource resource;
HtmlRenderPolicy htmlRenderPolicy = new HtmlRenderPolicy();
ConfigureBuilder builder = Configure.builder();
Configure config = builder.build();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Map data = new HashMap(8);
data.put("caseTitle", caseInfoVO.getCaseTitle());
data.put("typeName", caseInfoVO.getTypeName());
resource = new ClassPathResource("document" + File.separator + "word" + File.separator + "导出模板.docx");
config.customPolicy("criminalBaseInfoSituation", htmlRenderPolicy);
data.put("criminalBaseInfoSituation", dealWithPictureWidthAndHeight(caseInfoVO.getCriminalBaseInfoSituation()));
//输出到浏览器|下载到本地路径
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(caseInfoVO.getTenantName()).append("-").append(caseInfoVO.getTypeName()).append("-《").append(caseInfoVO.getCaseTitle()).append("》").append("案例");
response.setContentType("application/octet-stream");
response.setHeader("Content-disposition", "attachment;filename=\"" + new String(stringBuilder.toString().getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1) + ".docx" + "\"");
OutputStream out = response.getOutputStream();
XWPFTemplate.compile(resource.getInputStream(), config).render(data).writeAndClose(out);
out.flush();
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
//处理图片超过word宽度问题,等比缩小
private String dealWithPictureWidthAndHeight(String content) {
List> imagesFiles = HtmlUtil.regexMatchWidthAndHeight(content);
if (Func.isNotEmpty(imagesFiles)) {
for (HashMap imagesFile : imagesFiles) {
String newFileUrl = imagesFile.get("newFileUrl");
String fileUrl = imagesFile.get("fileUrl");
if (Func.isNotEmpty(newFileUrl)){
content = content.replace(fileUrl, newFileUrl);
}
}
}
return content;
}
如果觉得文章对您有帮助,麻烦点个赞再走哈
搬运麻烦标注出处