Prechádzať zdrojové kódy

通过html表格代码生成word表格文档

wucl 9 mesiacov pred
rodič
commit
8b9594ad3d

+ 7 - 0
biz-base/pom.xml

@@ -39,6 +39,13 @@
             <artifactId>spring-boot-starter-test</artifactId>
             <scope>test</scope>
         </dependency>
+
+        <dependency>
+            <groupId>com.aspose</groupId>
+            <artifactId>aspose-words</artifactId>
+        </dependency>
+
+
     </dependencies>
     <dependencyManagement>
         <dependencies>

+ 2 - 0
biz-base/src/main/resources/application-local.yaml

@@ -63,3 +63,5 @@ sa-token:
 
 # oa系统的api url
 oa-api-url: localhost:8088/api/
+
+tableSavePath: /Users/wuwei/opt/temp/

Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 1538 - 0
biz-base/src/main/resources/ftl/wordTableTemplate.ftl


Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 28 - 0
biz-base/src/test/java/com/dayou/FreeMarkTest.java


+ 9 - 0
common/pom.xml

@@ -79,7 +79,16 @@
             <groupId>com.aspose</groupId>
             <artifactId>aspose-words</artifactId>
         </dependency>
+        <dependency>
+            <!-- jsoup HTML parser library @ https://jsoup.org/ -->
+            <groupId>org.jsoup</groupId>
+            <artifactId>jsoup</artifactId>
+        </dependency>
 
+        <dependency>
+            <groupId>org.freemarker</groupId>
+            <artifactId>freemarker</artifactId>
+        </dependency>
 
     </dependencies>
     <dependencyManagement>

+ 56 - 0
common/src/main/java/com/dayou/constants/TableHTML.java

@@ -0,0 +1,56 @@
+package com.dayou.constants;
+
+/**
+ * 类说明:
+ *
+ * @author: wucl
+ * @since: 2023/9/15
+ * created with IntelliJ IDEA.
+ */
+public class TableHTML {
+
+    public static String TABLE = "<table  " +
+            " style= \"width:5.77in;border-collapse:collapse; border-spacing:\"0\"; empty-cells:show ;border=\"0\"; cellspacing=\"0\"; cellpadding=\"0\" \"> "+
+            "<caption style=\" border-bottom-style:solid; border-bottom-color:#FFFFFF; " +
+            "border-top-style:solid;border-top-color:#FFFFFF;" +
+            "border-left-style:solid;border-left-color:#FFFFFF;" +
+            "border-right-style:solid;border-right-color:#FFFFFF;text-align: left; vertical-align: top;\">" +
+            "<span style=\"font-size:12px;\">表tableNumber</span>" +
+            "</caption>" +
+            "<tbody class=\"tbody\">" +
+            "<tr class=\"tableHead\">" +
+            "</tr>" +
+            "</tbody>" +
+            "</table>";
+
+    public static String TR = "<tr>" +
+            "                   </tr>";
+    public static String TD_HEAD = "<th style=\"font-size:14px;border-top-style:solid; border-top-color:#000000; " +
+            "border-left-style:solid; border-left-color:#000000;border-bottom-style:solid; border-bottom-color:#000000;border-right-style:solid; " +
+            "border-right-color:#000000;background-color:#d7d7d7;padding-left:0.0035in; padding-right:0.0035in; padding-top:0in; padding-bottom:0in;" +
+            "vertical-align:middle; width:auto \" >" +
+                    "<p style=\"text-align: center; text-indent: 0em; line-height: normal; \" ><span style=\"text-align: -webkit-center; text-wrap: wrap;\">" +
+                    "content" +
+                    "</span></p>" +
+                    "</th>";
+
+    public static String TD = "<td valign=\"middle\" style=\"font-size:14px;border-top-style:solid; border-top-color:#000000; " +
+            "border-left-style:solid; border-left-color:#000000;border-bottom-style:solid; border-bottom-color:#000000;" +
+            "border-right-style:solid; border-right-color:#000000;background-color:transparent; padding-left:0.0035in; padding-right:0.0035in; padding-top:0in; padding-bottom:0in; " +
+            "vertical-align:middle;  width:auto \" >" +
+                    "<p style=\"text-align: center; text-indent: 0em; line-height: normal; \" ><span style=\"text-align: -webkit-center; text-wrap: wrap;\">" +
+                    "content" +
+                    "</span></p>" +
+                    "</td>";
+
+    public static final String TABLE_STYLE = "width:109.9%;border-collapse:collapse; border-spacing:0; empty-cells:show;";
+
+    public static final String TD_STYLE = "font-size:9px;border-top-style:solid; border-top-color:#000000; " +
+            "border-left-style:solid; border-left-color:#000000;border-bottom-style:solid; border-bottom-color:#000000;" +
+            "border-right-style:solid; border-right-color:#000000;background-color:transparent; padding-left:0.0035in; padding-right:0.0035in; padding-top:0in; padding-bottom:0in; " +
+            "vertical-align:middle;";
+
+    public static String P_SPAN = "<p style = \"text-align: center; text-indent: 0em; line-height: normal;\" ><span style=\"text-align: -webkit-center; text-wrap: wrap;\">content</span> </p> " ;
+
+    public static final String SPAN_STYLE = "text-align: -webkit-center; text-wrap: wrap;";
+}

+ 83 - 0
common/src/main/java/com/dayou/utils/table/FreemarkerUtil.java

@@ -0,0 +1,83 @@
+package com.dayou.utils.table;
+
+import freemarker.template.Configuration;
+import freemarker.template.Template;
+import freemarker.template.TemplateException;
+
+import java.io.*;
+import java.util.Map;
+
+/**
+ * 类说明:
+ *
+ * @author: wucl
+ * @since: 2023/8/31
+ * created with IntelliJ IDEA.
+ */
+public class FreemarkerUtil {
+
+    private  Configuration configuration = null;
+    /**
+     * 获取单例对象
+     *
+     * @param templatePath ftl模板文件所在路径
+     * @return
+     * @throws IOException
+     */
+    private Configuration getInstance(String templatePath) throws IOException {
+        if (configuration == null) {
+            configuration = new Configuration(Configuration.getVersion());
+//            //server
+//            configuration.setClassForTemplateLoading(this.getClass(),templatePath);
+            //location
+            configuration.setDirectoryForTemplateLoading(new File(templatePath));
+            configuration.setDefaultEncoding("utf-8");
+        }
+        return configuration;
+    }
+
+    /**
+     * 获取模板对象
+     *
+     * @param templateName 模板文件名称
+     * @return
+     */
+    private Template getTemplate(String templateName) {
+        try {
+            Template template = configuration.getTemplate(templateName);
+            template.setEncoding("UTF-8");
+            return template;
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    /**
+     * 基于文件的输出
+     *
+     * @param templatePath 模板所在路径 xxx/xxx/template
+     * @param templateName 模板名字 xxx.ftl
+     * @param root      数据集合
+     * @param outPath      输出文件路径  xxx/xxx/xxx.doc
+     */
+    public void fprint(String templatePath, String templateName, Map<String, String> root, String outPath) {
+        try {
+            getInstance(templatePath);
+            Template template = getTemplate(templateName);
+            Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(new File(outPath)), "UTF-8"));
+            template.process(root, out);
+            out.close();
+        } catch (FileNotFoundException e) {
+            e.printStackTrace();
+            throw new RuntimeException(e);
+        } catch (TemplateException e) {
+            e.printStackTrace();
+            throw new RuntimeException(e);
+        } catch (IOException e) {
+            e.printStackTrace();
+            throw new RuntimeException(e);
+        }
+    }
+
+}

+ 97 - 0
common/src/main/java/com/dayou/utils/table/RichHtmlHandler.java

@@ -0,0 +1,97 @@
+package com.dayou.utils.table;
+
+import cn.hutool.core.util.StrUtil;
+import org.jsoup.Jsoup;
+import org.jsoup.nodes.Document;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 类说明: 富文本Html处理器
+ *
+ * @author: wucl
+ * @since: 2023/8/31
+ * created with IntelliJ IDEA.
+ */
+public class RichHtmlHandler {
+    private String handledDocBodyBlock; //富文本所在doc中的区域
+    private List<String> docBase64BlockResults = new ArrayList<String>(); //文档中图片64位编码的结果区
+    private RichObject richObject = null; //富文本对象
+    private Document doc = null; //默认文档为空
+
+    public RichHtmlHandler(){
+        super();
+    }
+
+    public RichHtmlHandler(RichObject richObj) throws IOException {
+        this.richObject = richObj;
+        doc = Jsoup.parse(richObject.getHtml());
+    }
+
+
+    /**
+     * 获取样式的属性值
+     * @param style
+     * @param attributeKey
+     * @return
+     */
+    private String getStyleAttrValue(String style, String attributeKey) {
+        if (StrUtil.isEmpty(style)) {
+            return "";
+        }
+        // 以";"分割
+        String[] styleAttrValues = style.split(";");
+        for (String item : styleAttrValues) {
+            // 在以 ":"分割
+            String[] keyValuePairs = item.split(":");
+            if (attributeKey.equals(keyValuePairs[0])) {
+                return keyValuePairs[1];
+            }
+        }
+        return "";
+    }
+
+    /**
+     * 给html添加必须的标签
+     * @param html
+     * @return
+     */
+    private String wrappHtml(String html){
+        // 因为传递过来都是不完整,需要添加以下标签
+        StringBuilder sb = new StringBuilder();
+        sb.append("<html>");
+        sb.append("<body>");
+        sb.append(html);
+        sb.append("</body>");
+        sb.append("</html>");
+        return sb.toString();
+    }
+
+    public String getHandledDocBodyBlock() {
+        String raw = WordHtmlGeneratorHelper.string2Ascii(doc.getElementsByTag("body").html());
+        return raw.replace("=3D", "=").replace("=", "=3D");
+    }
+
+    public void setHandledDocBodyBlock(String handledDocBodyBlock) {
+        this.handledDocBodyBlock = handledDocBodyBlock;
+    }
+
+    public List<String> getDocBase64BlockResults() {
+        return docBase64BlockResults;
+    }
+
+    public void setDocBase64BlockResults(List<String> docBase64BlockResults) {
+        this.docBase64BlockResults = docBase64BlockResults;
+    }
+
+
+    public RichObject getRichObject() {
+        return richObject;
+    }
+
+    public void setRichObject(RichObject richObject) {
+        this.richObject = richObject;
+    }
+}

+ 25 - 0
common/src/main/java/com/dayou/utils/table/RichObject.java

@@ -0,0 +1,25 @@
+package com.dayou.utils.table;
+
+import lombok.Data;
+
+/**
+ * 类说明:
+ *
+ * @author: wucl
+ * @since: 2023/8/31
+ * created with IntelliJ IDEA.
+ */
+@Data
+public class RichObject {
+    private String html; //富文本html文件
+    //指doc转成mht之后的一些基础配置
+    private String docSrcParent = "risk.files"; //文档父类
+    private String docSrcLocationPrex = "file:///C:/268AB2CB"; //文档本地前缀
+    private String nextPartId = "01D9FD2C.A1F9C7B0"; //下一个父类
+    private String shapeidPrex = "_x56fe__x7247__x0020" ; //文档中shapeid前缀
+    private String spidPrex = "_x0000_i"; //文档中spid的前缀
+    private String typeid = "#_x0000_t75"; //类型ID
+    private Boolean isWebAppliction = false; //是不是web应用
+
+
+}

+ 36 - 0
common/src/main/java/com/dayou/utils/table/WordGeneratorWithFreemarker.java

@@ -0,0 +1,36 @@
+package com.dayou.utils.table;
+
+import java.util.Map;
+
+/**
+ * 类说明:
+ *
+ * @author: wucl
+ * @since: 2023/8/31
+ * created with IntelliJ IDEA.
+ */
+public class WordGeneratorWithFreemarker {
+
+    /**
+     * 创建doc文件
+     * @param templatePath 模板所在路径 xxx/xxx/template
+     * @param templateName 模板名字 xxx.ftl
+     * @param dataMap 数据集合
+     * @param outPath 输出文件路径  xxx/xxx/xxx.doc
+     */
+    public static void createDoc(String templatePath, String templateName, Map<String, String> dataMap, String outPath) throws Exception{
+        FreemarkerUtil freemarker = new FreemarkerUtil();
+        freemarker.fprint(templatePath,templateName,dataMap,outPath);
+    }
+
+    /**
+     * 创建富文本Html处理器,主要处理图片及编码
+     * @param richObject 需要的参数
+     * @return
+     */
+    public static RichHtmlHandler createRichHtmlHandler(RichObject richObject) throws Exception{
+        RichHtmlHandler richHtmlHandler = new RichHtmlHandler(richObject);
+        return richHtmlHandler;
+    }
+
+}

+ 157 - 0
common/src/main/java/com/dayou/utils/table/WordHtmlGeneratorHelper.java

@@ -0,0 +1,157 @@
+package com.dayou.utils.table;
+
+import cn.hutool.core.util.StrUtil;
+import org.springframework.util.ReflectionUtils;
+
+import java.lang.reflect.Field;
+import java.util.Collection;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 类说明:
+ *
+ * @author: wucl
+ * @since: 2023/8/31
+ * created with IntelliJ IDEA.
+ */
+public class WordHtmlGeneratorHelper {
+
+    /**
+     * 将字符换成3Dus-asci,十进制Accsii码
+     * @param source
+     * @return
+     */
+    public static String string2Ascii(String source){
+        if(source==null || source==""){
+            return null;
+        }
+        StringBuilder sb=new StringBuilder();
+
+        char[] c=source.toCharArray();
+        for(char item : c){
+            String itemascii="";
+            if(item>=19968 && item<40623){
+                itemascii = itemascii = "&#"+(item & 0xffff)+";";
+            }else{
+                itemascii = item+"";
+            }
+            sb.append(itemascii);
+        }
+
+        return sb.toString();
+
+    }
+
+    /**
+     * 将object的所有属性值转成成3Dus-asci编码值
+     * @param toHandleObject
+     * @param <T>
+     * @return
+     */
+    public static <T extends Object> T handleObject2Ascii(final T toHandleObject){
+
+        class myFieldsCallBack  implements ReflectionUtils.FieldCallback {
+
+            @Override
+            public void doWith(Field f) throws IllegalArgumentException,
+                    IllegalAccessException {
+                if(f.getType().equals(String.class)){
+                    //如果是字符串类型
+                    f.setAccessible(true);
+                    String oldValue=(String)f.get(toHandleObject);
+                    if(!StrUtil.isEmpty(oldValue)){
+                        f.set(toHandleObject, string2Ascii(oldValue));
+                    }
+                    //f.setAccessible(false);
+                }
+            }
+        }
+
+        ReflectionUtils.doWithFields(toHandleObject.getClass(), new myFieldsCallBack());
+
+        return toHandleObject;
+    }
+
+    /**
+     * list所有属性值转成成3Dus-asci编码值
+     * @param toHandleObjects
+     * @param <T>
+     * @return
+     */
+    public static <T extends Object> List<T> handleObjectList2Ascii(final List<T> toHandleObjects){
+
+        for (T t : toHandleObjects) {
+            handleObject2Ascii(t);
+        }
+
+        return toHandleObjects;
+    }
+
+    /**
+     * 处理所有的数据
+     * @param dataMap
+     */
+    public static void handleAllObject(Map<String, Object> dataMap){
+        //去处理数据
+        for (Map.Entry<String, Object> entry : dataMap.entrySet()){
+            Object item=entry.getValue();
+
+            //判断object是否是primitive type
+            if(isPrimitiveType(item.getClass())){
+                if(item.getClass().equals(String.class)){
+                    item=WordHtmlGeneratorHelper.string2Ascii((String)item);
+                    entry.setValue(item);
+                }
+            }else if(isCollection(item.getClass())){
+                for (Object itemobject : (Collection)item) {
+                    WordHtmlGeneratorHelper.handleObject2Ascii(itemobject);
+                }
+            }else{
+                WordHtmlGeneratorHelper.handleObject2Ascii(item);
+            }
+        }
+
+    }
+
+    /**
+     * 拼接数据
+     * @param list
+     * @param join
+     * @return
+     */
+    public static String joinList(List<String> list,String join ){
+        StringBuilder sb=new StringBuilder();
+        for (String t : list) {
+            sb.append(t);
+            if(!StrUtil.isEmpty(join)){
+                sb.append(join);
+            }
+        }
+        return sb.toString();
+    }
+
+
+    /**
+     * 判断是否是原始类型
+     * @param clazz
+     * @return
+     */
+    private static boolean isPrimitiveType(Class<?> clazz){
+        return clazz.isEnum() ||
+                CharSequence.class.isAssignableFrom(clazz) ||
+                Number.class.isAssignableFrom(clazz) ||
+                Date.class.isAssignableFrom(clazz);
+
+    }
+
+    /**
+     * 判断是否是集合类
+     * @param clazz
+     * @return
+     */
+    private static boolean isCollection(Class<?> clazz){
+        return Collection.class.isAssignableFrom(clazz);
+    }
+}

+ 68 - 0
common/src/main/java/com/dayou/utils/table/WordTableHelper.java

@@ -0,0 +1,68 @@
+package com.dayou.utils.table;
+
+import cn.hutool.core.util.StrUtil;
+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.Value;
+import org.springframework.stereotype.Component;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static com.dayou.constants.TableHTML.*;
+
+@Component
+public class WordTableHelper {
+
+    @Value("${tableSavePath}")
+    private String tableSavePath;
+
+    private final String FTL_TEMPLATE_DIR = "/ftl";
+
+    private final String FTL_TEMPLATE = "wordTableTemplate.ftl";
+
+
+    /**
+     *
+     * @param html excel表格解析后的html代码
+     * @return 生成的word表格的存储路径
+     */
+    public String createTableWord(String html){
+
+        try {
+            if (StrUtil.isNotBlank(html)){
+                RichObject richObject = new RichObject();
+                richObject.setHtml(htmlTableFormat(html));
+                RichHtmlHandler richHtmlHandler = WordGeneratorWithFreemarker.createRichHtmlHandler(richObject);
+
+                Map<String,String> data = new HashMap<>();
+                data.put("table", richHtmlHandler.getHandledDocBodyBlock());
+                String tablePath = tableSavePath + System.currentTimeMillis()+".docx";
+                String templatePath = Class.class.getResource(FTL_TEMPLATE_DIR).getPath();
+                templatePath = java.net.URLDecoder.decode(templatePath,"utf-8");
+                WordGeneratorWithFreemarker.createDoc(templatePath,FTL_TEMPLATE,data,tablePath);
+                return tablePath;
+            }
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+        return null;
+    }
+
+    public String htmlTableFormat(String srcHtml){
+        Document document = Jsoup.parse(srcHtml);
+        Elements table = document.getElementsByTag("table");
+        table.attr("style",TABLE_STYLE);
+        Elements td = document.select("table td");
+        td.removeAttr("width");
+        td.attr("style",TD_STYLE);
+        for (Element element : td){
+            String text = element.text();
+            element.text("");
+            element.append(P_SPAN.replace("content",text));
+        }
+        return document.body().html();
+    }
+}

+ 14 - 0
pom.xml

@@ -37,6 +37,8 @@
         <poi-ooxml.version>5.2.3</poi-ooxml.version>
         <commons-io.version>2.15.0</commons-io.version>
         <aspose-words.version>20.12</aspose-words.version>
+        <jsoup.version>1.11.3</jsoup.version>
+        <freemarker.version>2.3.29</freemarker.version>
     </properties>
 
     <dependencyManagement>
@@ -144,6 +146,18 @@
                 <version>${aspose-words.version}</version>
             </dependency>
 
+            <dependency>
+                <!-- jsoup HTML parser library @ https://jsoup.org/ -->
+                <groupId>org.jsoup</groupId>
+                <artifactId>jsoup</artifactId>
+                <version>${jsoup.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>org.freemarker</groupId>
+                <artifactId>freemarker</artifactId>
+                <version>${freemarker.version}</version>
+            </dependency>
         </dependencies>
     </dependencyManagement>