文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

java实现word导入导出富文本(含图片)-附完整测试用例

2023-08-16 18:06

关注

主要有以下几点:

解决富文本导入导出依赖兼容问题
2、处理富文本和非富文本内容
3、解决webp格式通过java下载不了问题,如果要用到富文本导出,将来势必是会碰到的bug,这里提前给提出来并解决,测试用例中有给图片测试。
4、在原有方法上优化,比如处理等比缩小图片、将图片本地路径,替换为minio或者base64格式

gitee测试用例:
链接: https://gitee.com/muyangrenOvo/word-import-export

注意:与文章代码有出入,但思路是一样的。只是获取文件的方式变了,一个是前端调用组件传的,一个是自己new file。

1)引入pom.xml依赖

    org.apache.poi    poi    4.1.2    org.apache.poi    poi-ooxml-schemas    4.1.2    org.apache.poi    poi-ooxml    4.1.2    fr.opensagres.xdocreport    xdocreport    2.0.2    org.apache.poi    poi-scratchpad    4.1.2io.github.draco1023poi-tl-ext0.4.2    org.jsoup    jsoup    1.15.3      com.github.nintha     webp-imageio-core     0.1.0                

2) word文档导入带样式(含图片)

例如这是word文档,我们要通过波浪线去截取对应内容
在这里插入图片描述

Controller层

    @ApiLog("导入模板")    @PostMapping("/importTemplate")    @ApiOperation(value = "导入模板", notes = "传file")    public R importCase(@RequestParam MultipartFile file) {        return R.data(caseInfoService.importTemplate(file));    }

service层

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;@Overridepublic CaseInfoVO importTemplate(MultipartFile file){        try {            caseInfoVO = new CaseInfoVO();            //1、处理非富文本内容基本信息(讲解的是富文本导入,所以该内容略过)            //List> mapList = WordUtil.readWord(file);            //assert mapList != null;            //dealWithCaseBasicInfo(caseInfoVO, mapList);            //2、下载文件到本地            File destFile = fileDownloadToLocalPath(file);            //3、处理案例富文本信息            dealWithCaseInfoRichText(caseInfoVO, destFile);            //4、替换案例富文本信息中的图片(如果有)路径并删除临时文件和临时图片            dealWithCaseInfoRichTextToPicture(caseInfoVO);        } catch (Exception e) {            e.printStackTrace();        }        return caseInfoVO;    }    private void dealWithCaseInfoRichText(CaseInfoVO caseInfoVO, File destFile) {        if (!destFile.exists()) {            throw new ServiceException("导入模板失败,请重新上传!");        } else {            //判断是否为docx文件            if (destFile.getName().endsWith(".docx") || destFile.getName().endsWith(".DOCX")) {                // 1)加载word文档生成XWPFDocument对象                try (FileInputStream in = new FileInputStream(destFile); XWPFDocument document = new XWPFDocument(in)) {                    // 2)解析XHTML配置(这里设置IURIResolver来设置图片存放的目录)                    File imageFolderFile = new File(String.valueOf(destFile.getParentFile()));                    XHTMLOptions options = XHTMLOptions.create().URIResolver(new FileURIResolver(imageFolderFile));                    options.setExtractor(new FileImageExtractor(imageFolderFile));                    options.setIgnoreStylesIfUnused(false);                    options.setFragment(true);                    //使用字符数组流获取解析的内容                    ByteArrayOutputStream baos = new ByteArrayOutputStream();                    XHTMLConverter.getInstance().convert(document, baos, options);                  //带样式的内容(富文本)                    String conTent = baos.toString();                  //通过波浪线分割,然后通过debug去看自己需要的内容的下标位置 然后获取即可(如果不懂,私信)                    String[] tableSplit = conTent.split("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

"); int length = tableSplit.length; //最好是判断下length预期长度,否则模板用于定位的波浪线给破坏了,拿的内容也就变了 caseInfoVO.setCriminalBaseInfoSituation(tableSplit[2]); caseInfoVO.setCriminalEducationTransformPlan(tableSplit[4]); caseInfoVO.setCriminalEducationTransformResult(tableSplit[6]); } } catch (IOException | XWPFConverterException e) { e.printStackTrace(); } finally { FileUtil.deleteQuietly(destFile); } } } } private String dealWithCaseInfoRichTextToPictureChild(String content, OssBuilder ossBuilder,Set files) { List imagesFiles = HtmlUtil.regexMatchPicture(content); if (Func.isNotEmpty(imagesFiles)) { for (String imagesFile : imagesFiles) { File file = new File(imagesFile); MultipartFile fileItem = createFileItem(file, file.getName()); boolean aBoolean = true; //此处选择循环调用,避免minio上传失败返回空(主要看需求)。 while (Boolean.TRUE.equals(aBoolean)) { BladeFile bladeFile = ossBuilder.template().putFile(fileItem); if (Func.isNotEmpty(bladeFile)) { String link = bladeFile.getLink(); content = content.replace(imagesFile, link); //删除临时图片(统一删除 如上传同一张图片,第二次会找不到图片) files.add(file); aBoolean = false; } } } } return content; }//最好是定义一个工具类,这里图看起来比较直观,就单独拿出来了 public File fileDownloadToLocalPath(MultipartFile file) { File destFile = null; try { String fileName = file.getOriginalFilename(); //获取文件后缀 String pref = fileName.lastIndexOf(".") != -1 ? fileName.substring(fileName.lastIndexOf(".") + 1) : null; //临时文件 //临时文件名避免重复 String uuidFile = UUID.randomUUID().toString().replace("-", "") + "." + pref; destFile = new File(FileChangeUtils.getProjectPath() + uuidFile); if (!destFile.getParentFile().exists()) { destFile.getParentFile().mkdirs(); } file.transferTo(destFile); } catch (IOException e) { e.printStackTrace(); } return destFile; }public MultipartFile createFileItem(File file, String fieldName) {FileItemFactory factory = new DiskFileItemFactory(16, null);FileItem item = factory.createItem(fieldName, ContentType.MULTIPART_FORM_DATA.toString(), true, file.getName());int bytesRead = 0;byte[] buffer = new byte[8192];try {FileInputStream fis = new FileInputStream(file);OutputStream os = item.getOutputStream();while ((bytesRead = fis.read(buffer, 0, 8192)) != -1) {os.write(buffer, 0, bytesRead);}os.close();fis.close();} catch (IOException e) {e.printStackTrace();}return new CommonsMultipartFile(item);}

HtmlUtil工具类

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;public class HtmlUtil {        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;    }        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;    }}

3) 富文本导出word文档(含图片)

参考文献
链接: https://github.com/draco1023/poi-tl-ext
模板如图所示
在这里插入图片描述

Controller层

    @ApiLog("模板-下载")    @GetMapping("/downloadTemplate")    @ApiOperation(value = "模板-下载")    public void downloadCaseInfo(HttpServletResponse response,CaseInfoDTO caseInfoDTO) {        caseInfoService.downloadTemplate(response,caseInfoDTO);    }

Service层

   @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;    }

如果觉得文章对您有帮助,麻烦点个赞再走哈
搬运麻烦标注出处

来源地址:https://blog.csdn.net/m0_51706962/article/details/128818754

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯