NBA
resulttransformer(Ueditor二次编辑word(doc和docx格式),SpringBoot后端)

vue+ueditor+springboot, 实现word文档上传编辑

前言

`前端导入word文档(doc和docx格式都支持),Ueditor富文本回显进行二次编辑,目前ueditor项目archived了,实现两种格式的相关材料相对稀缺。

`解决思路:

1.上传word文件

2.后台读取word内容(图片需要额外处理保存到服务器固定的地址,该地址能让浏览器直接访问),生成html文件

3.后台读取html文件内容返回给前端


一、目标

通过上传word文件,通过后台进行解析回显到前端。



二、代码步骤

后端代码结构:


1.maven依赖库

<dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>3.15</version></dependency><dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>3.15</version></dependency><dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml-schemas</artifactId><version>3.15</version></dependency><dependency><groupId>org.apache.poi</groupId><artifactId>poi-scratchpad</artifactId><version>3.15</version></dependency><dependency><groupId>fr.opensagres.xdocreport</groupId><artifactId>org.apache.poi.xwpf.converter.xhtml</artifactId><version>1.0.6</version></dependency>

2.vue页面读取ueditor的配置

代码如下:

@GetMapping(value = "/config")public void ueConfig(HttpServletRequest request, HttpServletResponse response) throws IOException {response.setContentType("application/json");response.setCharacterEncoding("utf-8");String urlPrefix = ueProperties.getSavepath();log.info("urlPrefix = "+urlPrefix);String exec = "{\n" +" \n" +" \"imageActionName\": \"catcherImage\", \n" +" \"imageFieldName\": \"upfile\", \n" +" \"imageMaxSize\": 2048, \n" +" \"imageAllowFiles\": [\".png\", \".jpg\", \".jpeg\", \".gif\", \".bmp\"], \n" +" \"imageCompressEnable\": true, \n" +" \"imageCompressBorder\": 800, \n" +" \"imageInsertAlign\": \"none\", \n" +" \"imageUrlPrefix\": \"" + urlPrefix + "\", \n" +" \"imagePathFormat\": \"/ueditor/image/{yyyy}{mm}{dd}/\", \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +"\n" +" \n" +" \"scrawlActionName\": \"uploadscrawl\", \n" +" \"scrawlFieldName\": \"upfile\", \n" +" \"scrawlPathFormat\": \"/ueditor/image/{yyyy}{mm}{dd}/\", \n" +" \"scrawlMaxSize\": 2048000, \n" +" \"scrawlUrlPrefix\": \"\", \n" +" \"scrawlInsertAlign\": \"none\",\n" +"\n" +" \n" +" \"snapscreenActionName\": \"uploadimage\", \n" +" \"snapscreenPathFormat\": \"/ueditor/image/{yyyy}{mm}{dd}/\", \n" +" \"snapscreenUrlPrefix\": \"\", \n" +" \"snapscreenInsertAlign\": \"none\", \n" +"\n" +" \n" +" \"catcherLocalDomain\": [\"127.0.0.1\", \"localhost\", \"img.baidu.com\"],\n" +" \"catcherActionName\": \"catchimage\", \n" +" \"catcherFieldName\": \"source\", \n" +" \"catcherPathFormat\": \"/ueditor/image/{yyyy}{mm}{dd}/\", \n" +" \"catcherUrlPrefix\": \"" + urlPrefix + "\", \n" +" \"catcherMaxSize\": 2048000, \n" +" \"catcherAllowFiles\": [\".png\", \".jpg\", \".jpeg\", \".gif\", \".bmp\"], \n" +"\n" +" \n" +" \"videoActionName\": \"uploadvideo\", \n" +" \"videoFieldName\": \"upfile\", \n" +" \"videoPathFormat\": \"/ueditor/video/{yyyy}{mm}{dd}/\", \n" +" \"videoUrlPrefix\": \"\", \n" +" \"videoMaxSize\": 10240000, \n" +" \"videoAllowFiles\": [\n" +" \".flv\", \".swf\", \".mkv\", \".avi\", \".rm\", \".rmvb\", \".mpeg\", \".mpg\",\n" +" \".ogg\", \".ogv\", \".mov\", \".wmv\", \".mp4\", \".webm\", \".mp3\", \".wav\", \".mid\"], \n" +" \n" +" \"fileActionName\": \"uploadfile\", \n" +" \"fileFieldName\": \"upfile\", \n" +" \"filePathFormat\": \"/ueditor/file/{yyyy}{mm}{dd}/\", \n" +" \"fileUrlPrefix\": \"\", \n" +" \"fileMaxSize\": 10240000, \n" +" \"fileAllowFiles\": [\n" +" \".png\", \".jpg\", \".jpeg\", \".gif\", \".bmp\",\n" +" \".flv\", \".swf\", \".mkv\", \".avi\", \".rm\", \".rmvb\", \".mpeg\", \".mpg\",\n" +" \".ogg\", \".ogv\", \".mov\", \".wmv\", \".mp4\", \".webm\", \".mp3\", \".wav\", \".mid\",\n" +" \".rar\", \".zip\", \".tar\", \".gz\", \".7z\", \".bz2\", \".cab\", \".iso\",\n" +" \".doc\", \".docx\", \".xls\", \".xlsx\", \".ppt\", \".pptx\", \".pdf\", \".txt\", \".md\", \".xml\"\n" +" ], \n" +" \n" +" \"imageManagerActionName\": \"listimage\", \n" +" \"imageManagerListPath\": \"/ueditor/image/{yyyy}{mm}{dd}/\", \n" +" \"imageManagerListSize\": 20, \n" +" \"imageManagerUrlPrefix\": \"" + urlPrefix + "\", \n" +" \"imageManagerInsertAlign\": \"none\", \n" +" \"imageManagerAllowFiles\": [\".png\", \".jpg\", \".jpeg\", \".gif\", \".bmp\"], \n" +" \n" +" \"fileManagerActionName\": \"listfile\", \n" +" \"fileManagerListPath\": \"/ueditor/file/{yyyy}{mm}{dd}/\", \n" +" \"fileManagerUrlPrefix\": \"\", \n" +" \"fileManagerListSize\": 20, \n" +" \"fileManagerAllowFiles\": [\n" +" \".png\", \".jpg\", \".jpeg\", \".gif\", \".bmp\",\n" +" \".flv\", \".swf\", \".mkv\", \".avi\", \".rm\", \".rmvb\", \".mpeg\", \".mpg\",\n" +" \".ogg\", \".ogv\", \".mov\", \".wmv\", \".mp4\", \".webm\", \".mp3\", \".wav\", \".mid\",\n" +" \".rar\", \".zip\", \".tar\", \".gz\", \".7z\", \".bz2\", \".cab\", \".iso\",\n" +" \".doc\", \".docx\", \".xls\", \".xlsx\", \".ppt\", \".pptx\", \".pdf\", \".txt\", \".md\", \".xml\"\n" +" ] \n" +"}";PrintWriter writer = response.getWriter();writer.write(exec);writer.flush();writer.close();}

```

3.前端导入文件

代码如下:

methods: {ready(editorInstance) {  this.editorInstance=editorInstance  async uploadWordFile(event) {  const file = event.target.files[0];  if (!file) return;  // 将Word文件转换为HTML  const htmlContent = await this.convertWordToHtml(file);  const jsonData = JSON.parse(htmlContent)  // 设置UEditor的内容  console.log(jsonData)  this.editorInstance.execCommand('inserthtml',jsonData.data)},async convertWordToHtml(wordFile) {  // 这里应该是Word文件转HTML的后端接口调用代码  // 假设有一个转换Word为HTML的后端API  const formData = new FormData();  formData.append('file', wordFile);  const response = await fetch('/api/ue/uploadFile',{  method:'POST',  body:formData})if (response.ok) {	return await response.text();}throw new Error('转换失败');}}


4.后端读取文件生成html

代码如下:

@PostMapping("/uploadFile")public Object uploadFile(@RequestParam(name = "file") MultipartFile file){  String filename = file.getOriginalFilename();  JSonObject result = new JSonObject();  String visitHtml = "";  try {    if (filename.endsWith(".docx")) {    //TODO 处理docx格式的    visitHtml = WordConverHtmlUtils.docxToHtmlText(file, ueProperties);    } else if (filename.endsWith(".doc")) {    //TODO 处理doc格式的    visitHtml = WordConverHtmlUtils.docToHtmlText(file, ueProperties);  } else {  	log.error("不支持的文件格式!");  }  result.put("state", "SUCCESS");  result.put("data", visitHtml);  log.info("result: {}", result.toString());  } catch (Exception e) { 		 log.error("文件找不到异常!");  	 e.printStackTrace();  }  return result;}

5.WordConverHtmlUtils工具类

⚠️⚠️⚠️⚠️⚠️

options.URIResolver(new BasicURIResolver(picUri));

picUri地址,必须能通过浏览器直接访问,否则编辑器中无法渲染出来图片; 比如作者:http://localhost:8000/resource/ueditor/file/20240404/1712220732312.png(本地搭建NG测试)

⚠️⚠️⚠️⚠️⚠️

代码如下:package com.ue.demo.utils;import cn.hutool.core.lang.UUID;import com.ue.demo.config.UeProperties;import lombok.extern.slf4j.Slf4j;import org.apache.poi.hwpf.HWPFdocument;import org.apache.poi.hwpf.converter.PicturesManager;import org.apache.poi.hwpf.converter.WordToHtmlConverter;import org.apache.poi.hwpf.usermodel.PictureType;import org.apache.poi.xwpf.converter.core.BasicURIResolver;import org.apache.poi.xwpf.converter.core.FileImageExtractor;import org.apache.poi.xwpf.converter.xhtml.XHTMLConverter;import org.apache.poi.xwpf.converter.xhtml.XHTMLOptions;import org.apache.poi.xwpf.usermodel.XWPFdocument;import org.springframework.web.multipart.MultipartFile;import org.w3c.dom.document;import javax.xml.parsers.documentBuilderFactory;import javax.xml.transform.OutputKeys;import javax.xml.transform.Transformer;import javax.xml.transform.TransformerFactory;import javax.xml.transform.dom.DOMSource;import javax.xml.transform.stream.StreamResult;import java.io.*;import java.nio.file.Files;import java.nio.file.Path;import java.nio.file.Paths;@Slf4j  public class WordConverHtmlUtils {        private final static String FILE_URL_PRE = "/ueditor/file/";                public static String docxToHtmlText(MultipartFile file, UeProperties ueProperties) throws Exception {        try {        String fileName = UUID.fastUUID().toString();        //图片存放地址        String imagePath = ueProperties.getSavepath().concat(FILE_URL_PRE).concat("/");        String fileOutName = imagePath.concat(fileName).concat(".html");        log.info("上传docx文档解析");        log.info("上传docx文档,返回解析后的Html, imagePath:{}", imagePath);        log.info("fileOutName:{}", fileOutName);        //获取一个用操作Word的对象        XWPFdocument document = new XWPFdocument(file.getInputStream());        //导出为html时的一些基本设置类        XHTMLOptions options = null;        //判断word文件中是否有图片        if(document.getAllPictures().size() > 0) {        //获取默认的对象,设置缩进indent        options = XHTMLOptions.getDefault().indent(4);        // 如果包含图片的话,要设置图片的导出位置        File imageFolder = new File(imagePath);        //设置图片抽取器的目的地文件夹 用于存放图片文件        options.setExtractor(new FileImageExtractor(imageFolder));        // URI resolver word的html中图片的目录路径        //⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️        //⚠️⚠️⚠️⚠️⚠️ 这里需要设置为前端能过直接访问到的图片地址, 比如作者:http://localhost:8000/resource/ueditor/file/20240404/1712220732312.png,        //⚠️⚠️⚠️⚠️⚠️ 否则,ueditor编辑器无法显示word文档中的图片        String picUri = ueProperties.getShowpath().concat(imagePath.substring(imagePath.indexOf("ueditor")));        options.URIResolver(new BasicURIResolver(picUri));      }      //获取输出的html文件对象      File outFile = new File(fileOutName);      if(!outFile.getParentFile().exists()){      outFile.getParentFile().mkdirs();      }      //创建所有的父路径,如果不存在父目录的话      outFile.getParentFile().mkdirs();      //创建一个输出流      OutputStream out = new FileOutputStream(outFile);      //html转换器      XHTMLConverter.getInstance().convert(document, out, options);      log.info("html转换器 success");      //处理生成的html,字符串形式给前端      return readHtmlStr(fileOutName);      } catch (Exception e) {      log.error("docxToHtmlText 解析异常", e);      }      return "";}public static String docToHtmlText(MultipartFile file, UeProperties ueProperties) throws Exception {      //使用字符数组流获取解析的内容      ByteArrayOutputStream baos = new ByteArrayOutputStream();      OutputStream outStream = new BufferedOutputStream(baos);      try {      String fileName = UUID.fastUUID().toString();      //将上传的文件传入document转换      //图片存放地址      String docPath = ueProperties.getSavepath().concat(FILE_URL_PRE).concat("/");      String imagePath = docPath.concat("image/");      String fileOutName = docPath.concat(fileName).concat(".html");      log.info("上传doc文档,返回解析 ");      log.info("fileOutName:{}", fileOutName);      //创建图片文件的存储目录      new File(imagePath).mkdirs();      //poi中doc文档对应的实体类      HWPFdocument hwpfdocument = new HWPFdocument(file.getInputStream());      //使用空的文档对象构建一个转换对象      WordToHtmlConverter converter = new WordToHtmlConverter(documentBuilderFactory      .newInstance()      .newdocumentBuilder()      .newdocument());      //设置存储图片的管理者--使用匿名内部类实现 该类实现了PicturesManager接口,实现了其中的savePicture方法      converter.setPicturesManager(new PicturesManager() {      FileOutputStream out = null;      //在下面的processdocument方法内部会调用该方法 用于存储word中的图片文件      @Override      public String savePicture(byte[] bytes, PictureType pictureType, String name, float width, float height) {      try {      //单个照片的保存      out = new FileOutputStream(imagePath + name);      out.write(bytes);      } catch (IOException exception) {      exception.printStackTrace();      }finally {      if(out != null) {      try {      out.close();      } catch (IOException e) {      e.printStackTrace();      }      }}//这里要返回给操作者(HtmldocumentFacade)一个存储的路径 用于生成Html时定位到图片资源//⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️//⚠️⚠️⚠️⚠️⚠️ 这里需要设置为前端能过直接访问到的图片地址, 比如作者:http://localhost:8000/resource/ueditor/file/20240404/1712220732312.png,//⚠️⚠️⚠️⚠️⚠️ 否则,ueditor编辑器无法显示word文档中的图片			return ueProperties.getShowpath().concat(imagePath.substring(imagePath.indexOf("ueditor"))).concat(name);      }      });      //使用外观模式,将hwpfdocument文档对象设置给HtmldocumentFacade中的document属性      converter.processdocument(hwpfdocument);      //获取转换器中的document文档      document htmldocument = converter.getdocument();      //充当文档对象模型 (DOM) 树形式的转换源树的持有者 -- 源树      DOMSource domSource = new DOMSource(htmldocument);      //转换器 该对象用于将源树转换为结果树      Transformer transformer = TransformerFactory.newInstance().newTransformer();      //设置输出时的以什么方式输出,也可说是结果树的文件类型 可以是html/xml/text或者是一些扩展前三者的扩展类型      transformer.setOutputProperty(OutputKeys.METHOD , "html");      //设置一些必要的属性 设置输出时候的编码为utf-8      transformer.setOutputProperty(OutputKeys.ENCODING , "utf-8");      //转换 将输入的源树转换为结果树并且输出到streamResult中      transformer.transform(domSource , new StreamResult(new File(fileOutName)));      log.info("html转换器 success");      //处理生成的html,字符串形式给前端      return readHtmlStr(fileOutName);      } catch (Exception e) {      log.error("docToHtmlText 异常", e);      } finally {      baos.close();      outStream.close();      }      return null;}private static String readHtmlStr(String htmlDirPath) throws IOException {log.info("处理生成的html,字符串形式给前端:{} ...Start..", htmlDirPath);String htmlStr = "";try {Path htmlPath = Paths.get(htmlDirPath);htmlStr = new String(Files.readAllBytes(htmlPath));htmlStr = htmlStr.replaceAll("\\n", "");htmlStr = htmlStr.replaceAll("\\s{2,}", " ");log.info("处理生成的html,字符串形式给前端。。。end");} catch (IOException e) {log.error("处理生成的html,字符串形式出错了, {}", e.getMessage());}return htmlStr;}}


6.后端配置文件

代码如下:

spring.application.name=ueserver.port=8000##UE编辑器配置#编辑器访问服务器的图片地址ue.showpath=http://localhost:8000/resource#ue文件存储路径前缀ue.savepath=/Users/cookie/documents/coding/uedemo```!!! ue.showpath=生产上有nginx需要在nginx.conf进行配置


三、实现效果

成功通过导入word文章,回显内容到ueditor编辑器


总结

*赠人玫瑰,手留余香*

源码地址:

https://gitee.com/gwancookie/uedemo

**读取word文档生成html借鉴:**

https://blog.csdn.net/qq_44717657/article/details/124497326


顶一下()     踩一下()

热门推荐

发表评论
0评