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 RimportCase(@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 ListregexMatchPicture(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 { //查询需要导入的数据 ListcaseInfoVOS = 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; }
如果觉得文章对您有帮助,麻烦点个赞再走哈
搬运麻烦标注出处