Java实现在线模版预览和下载

 

*** 所有目录参数皆为项目相对路径

一、基础准备

        1.1 Maven构建项目

        
            org.projectlombok
            lombok
            true
        
        
            org.apache.commons
            commons-lang3
            3.7
        
        
            org.apache.commons
            commons-io
            LATEST
        
        
            fr.opensagres.xdocreport
            fr.opensagres.xdocreport.document
            1.0.5
        
        
            fr.opensagres.xdocreport
            org.apache.poi.xwpf.converter.xhtml
            1.0.5
        
        
            org.apache.poi
            poi-scratchpad
            3.12
        

        1.2 获取资源绝对路径

 public static String getRealPath(String dirPath) {
        //利用资源加载器获取资源URL
        String path = Class.class.getResource("/").getPath();
        return path + dirPath;
    }

        1.3 获取目录下所有文件

 /**
     * 获取目录下所有文件(默认不存在目录下同时存在文件和文件夹)
     *
     * @param targetPath
     * @return
     */
    public static List getFiles(String targetPath) {
        String realPath = getRealPath(targetPath);
        List list = new ArrayList<>();
        if (StringUtils.isBlank(realPath)) {
            return null;
        }

        File file = new File(realPath);
        if (file.isFile()) {
            list.add(file);
            return list;
        } else {
            File[] files = file.listFiles();
            return files == null ? null : Arrays.asList(files);
        }
    }

        1.4 创建FTL模版

Java实现在线模版预览和下载_第1张图片

        1.5 获取填充数据

 /**
     * 注意dataMap里存放的数据Key值要与模板中的参数相对应
     *
     * @param target
     */
    private static Map getData(Object target) {
        Map map = new HashMap<>();
        Class beanClass = target.getClass();
        Field[] declaredFields = beanClass.getDeclaredFields();
        for (Field field : declaredFields) {
            field.setAccessible(true);
            String key = field.getName();
            Object value = null;
            try {
                value = field.get(target);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
            if(value == null) {
                value = "";
            }
            map.put(key, value);
        }
        return map;
    }

        1.6 向目标文件写入内容

    private static void writeFile(String content, String path) {
        FileOutputStream fos = null;
        BufferedWriter bw = null;
        try {
            File file = new File(path);
            if (!file.exists()) {
                boolean newFile = file.createNewFile();
            }
            fos = new FileOutputStream(file);
            bw = new BufferedWriter(new OutputStreamWriter(fos));
            bw.write(content);
        } catch (FileNotFoundException fnfe) {
            fnfe.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (bw != null) {
                    bw.close();
                }
                if (fos != null) {
                    fos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

二、根据用户输入填充ftl生成word文档

/**
     * 生成word文件
     *
     * @param templatePath
     * @param fileName
     * @param targetFilePath
     * @param target
     */
    public static void createDoc(String templatePath, String fileName, String targetFilePath,Object target) {
        //要填入模本的数据文件

        Map data = getData(target);
        //设置模本装置方法和路径,FreeMarker支持多种模板装载方法。可以重servlet,classpath,数据库装载,
        //这里我们的模板是放在com.phq.document.template包下面
        configuration.setClassForTemplateLoading(FileUtils.class, templatePath);
        Template t = null;
        try {
            //test.ftl为要装载的模板
            t = configuration.getTemplate(fileName);
        } catch (IOException e) {
            e.printStackTrace();
        }
        if(t == null) {
            return;
        }
        //输出文档路径及名称
        File outFile = new File(getRealPath(targetFilePath));
        Writer out = null;
        try {
            out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outFile)));
        } catch (FileNotFoundException e1) {
            e1.printStackTrace();
        }

        try {
            t.process(data, out);
        } catch (TemplateException | IOException e) {
            e.printStackTrace();
        }
    }

三、word文档转化为HTML实现在线预览

/**
     * 将word转换成html
     * 支持 .doc and .docx
     *
     * @param filePath       word路径
     * @param outPutFilePath html存储路径  形式如"/file/"
     * @param newFileName    html名
     * @throws TransformerException
     * @throws IOException
     * @throws ParserConfigurationException
     */
    public static void wordToHtml(String filePath, String outPutFilePath, String newFileName)
            throws TransformerException, IOException, ParserConfigurationException {

        String substring = filePath.substring(filePath.lastIndexOf(".") + 1);
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        outPutFilePath = getRealPath(outPutFilePath);
        filePath = getRealPath(filePath);

        //防止错误输入
        if(!outPutFilePath.endsWith("/") && !outPutFilePath.endsWith("\\")) {
            outPutFilePath = outPutFilePath + "/";
        }

        /*
         * word2007和word2003的构建方式不同,
         * 前者的构建方式是xml,后者的构建方式是dom树。
         * 文件的后缀也不同,前者后缀为.docx,后者后缀为.doc
         * 相应的,apache.poi提供了不同的实现类。
         */
        if ("docx".equals(substring)) {


            InputStream inputStream = new FileInputStream(new File(filePath));
            XWPFDocument document = new XWPFDocument(inputStream);

            //step 2 : prepare XHTML options
            final String imageUrl = "";

            XHTMLOptions options = XHTMLOptions.create();
            options.setExtractor(new FileImageExtractor(new File(outPutFilePath + imageUrl)));
            options.setIgnoreStylesIfUnused(false);
            options.setFragment(true);
            //    			@Override 重写的方法,加上这个报错,你看看是啥问题
            options.URIResolver(uri -> imageUrl + uri);

            //step 3 : convert XWPFDocument to XHTML
            XHTMLConverter.getInstance().convert(document, out, options);
        } else {
            HWPFDocument wordDocument = new HWPFDocument(new FileInputStream(filePath));
            WordToHtmlConverter wordToHtmlConverter = new WordToHtmlConverter(
                    DocumentBuilderFactory.newInstance().newDocumentBuilder()
                            .newDocument());
            wordToHtmlConverter.setPicturesManager((content, pictureType, suggestedName, widthInches, heightInches) -> suggestedName);
            wordToHtmlConverter.processDocument(wordDocument);
            //save pictures
            List pics = wordDocument.getPicturesTable().getAllPictures();
            if (pics != null) {
                for (int i = 0; i < pics.size(); i++) {
                    Picture pic = (Picture) pics.get(i);
                    System.out.println();
                    try {
                        pic.writeImageContent(new FileOutputStream(outPutFilePath
                                + pic.suggestFullFileName()));
                    } catch (FileNotFoundException e) {
                        e.printStackTrace();
                    }
                }
            }
            Document htmlDocument = wordToHtmlConverter.getDocument();
            DOMSource domSource = new DOMSource(htmlDocument);
            StreamResult streamResult = new StreamResult(out);

            TransformerFactory tf = TransformerFactory.newInstance();
            Transformer serializer = tf.newTransformer();
            serializer.setOutputProperty(OutputKeys.ENCODING, "utf-8");
            serializer.setOutputProperty(OutputKeys.INDENT, "yes");
            serializer.setOutputProperty(OutputKeys.METHOD, "html");
            serializer.transform(domSource, streamResult);
        }

        out.close();
        writeFile(new String(out.toByteArray()), outPutFilePath + newFileName);
    }

 四、解决Ftl模板生成doc文档的不足

        基于ftl模版生成的doc文档,其实际类型还是xml格式,在转化成HTML格式时失败,于是整合网上各类资料,以及自己的努力探索,得出以下方法来实现基于docx模板生成docx文档。

    /**
     * 实现word文档填充
     *
     * @param inFile
     *            word模板路径和名称
     * @param bean
     *            待填充的数据,从数据库读取
     * @throws IOException
     */
    public static void fillDocx(String inFile,String outFile, Object bean) throws IOException {
        Map data = getData(bean);
        InputStream is = new FileInputStream(FileUtils.getPath(inFile));
        XWPFDocument document;
        try {
            document = new XWPFDocument(OPCPackage.open(is));
            // 替换段落里面的变量
            replaceInPara(document, data);
            OutputStream os = new FileOutputStream(FileUtils.getPath(outFile));
            document.write(os);
            os.close();
            is.close();
        } catch (Exception e) {

            e.printStackTrace();
        }

    }

    /**
     * 替换段落里面的变量
     *
     * @param doc
     *            要替换的文档
     * @param params
     *            参数
     */
    private static void replaceInPara(XWPFDocument doc, Map params) {
        Iterator iterator = doc.getParagraphsIterator();
        XWPFParagraph para;
        while (iterator.hasNext()) {
            para = iterator.next();
            replaceInPara(para, params);
        }
    }

    /**
     * 替换段落里面的变量
     *
     * @param para
     *            要替换的段落
     * @param params
     *            参数
     */
    private static void replaceInPara(XWPFParagraph para, Map params) {
        List runs;
        Matcher matcher;
        String runText = "";

        if (pattern.matcher(para.getParagraphText()).find()) {
            runs = para.getRuns();
            if (runs.size() > 0) {
                int j = runs.size();
                for (int i = 0; i < j; i++) {
                    XWPFRun run = runs.get(0);
                    String i1 = run.toString();
                    runText += i1;
                    para.removeRun(0);

                }

            }
            matcher = pattern.matcher(runText);

            if (matcher.find()) {
                while ((matcher = pattern.matcher(runText)).find()) {
                    String value = String.valueOf(params.get(matcher.group(1)));
                    if (value.equals("null")) {
                        value = "";
                    }
                    runText = matcher.replaceFirst(value);
                }
                // 直接调用XWPFRun的setText()方法设置文本时,在底层会重新创建一个XWPFRun,把文本附加在当前文本后面,
                // 所以我们不能直接设值,需要先删除当前run,然后再自己手动插入一个新的run。
                para.insertNewRun(0).setText(runText);
            }
        }

    }



    /**
     * 将word转换成html
     * 支持 .doc and .docx
     *
     * @param filePath       word路径
     * @param outPutFilePath html存储路径  形式如"/file/"
     * @param newFileName    html名
     * @throws TransformerException
     * @throws IOException
     * @throws ParserConfigurationException
     */
    public static void wordToHtml(String filePath, String outPutFilePath, String newFileName)
            throws TransformerException, IOException, ParserConfigurationException {

        String substring = filePath.substring(filePath.lastIndexOf(".") + 1);
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        outPutFilePath = FileUtils.getPath(outPutFilePath);
        filePath = FileUtils.getPath(filePath);

        //防止错误输入
        if (!outPutFilePath.endsWith("/") && !outPutFilePath.endsWith("\\")) {
            outPutFilePath = outPutFilePath + "/";
        }

        /*
         * word2007和word2003的构建方式不同,
         * 前者的构建方式是xml,后者的构建方式是dom树。
         * 文件的后缀也不同,前者后缀为.docx,后者后缀为.doc
         * 相应的,apache.poi提供了不同的实现类。
         */
        if ("docx".equals(substring)) {


            InputStream inputStream = new FileInputStream(new File(filePath));
            XWPFDocument document = new XWPFDocument(inputStream);

            //step 2 : prepare XHTML options
            final String imageUrl = "";

            XHTMLOptions options = XHTMLOptions.create();
            options.setExtractor(new FileImageExtractor(new File(outPutFilePath + imageUrl)));
            options.setIgnoreStylesIfUnused(false);
            options.setFragment(true);
            //    			@Override 重写的方法,加上这个报错,你看看是啥问题
            options.URIResolver(uri -> imageUrl + uri);

            //step 3 : convert XWPFDocument to XHTML
            XHTMLConverter.getInstance().convert(document, out, options);
        } else {
            HWPFDocument wordDocument = new HWPFDocument(new FileInputStream(filePath));
            WordToHtmlConverter wordToHtmlConverter = new WordToHtmlConverter(
                    DocumentBuilderFactory.newInstance().newDocumentBuilder()
                            .newDocument());
            wordToHtmlConverter.setPicturesManager((content, pictureType, suggestedName, widthInches, heightInches) -> suggestedName);
            wordToHtmlConverter.processDocument(wordDocument);
            //save pictures
            List pics = wordDocument.getPicturesTable().getAllPictures();
            if (pics != null) {
                for (int i = 0; i < pics.size(); i++) {
                    Picture pic = pics.get(i);
                    System.out.println();
                    try {
                        pic.writeImageContent(new FileOutputStream(outPutFilePath
                                + pic.suggestFullFileName()));
                    } catch (FileNotFoundException e) {
                        e.printStackTrace();
                    }
                }
            }
            Document htmlDocument = wordToHtmlConverter.getDocument();
            DOMSource domSource = new DOMSource(htmlDocument);
            StreamResult streamResult = new StreamResult(out);

            TransformerFactory tf = TransformerFactory.newInstance();
            Transformer serializer = tf.newTransformer();
            serializer.setOutputProperty(OutputKeys.ENCODING, "utf-8");
            serializer.setOutputProperty(OutputKeys.INDENT, "yes");
            serializer.setOutputProperty(OutputKeys.METHOD, "html");
            serializer.transform(domSource, streamResult);
        }

        out.close();
        writeFile(new String(out.toByteArray()), outPutFilePath + newFileName);
    }

    /**
     * 注意dataMap里存放的数据Key值要与模板中的参数相对应
     *
     * @param target
     */
    private static Map getData(Object target) {
        if (target instanceof Map) {
            return (Map) target;
        }

        Map map = new HashMap<>();
        Class beanClass = target.getClass();
        Field[] declaredFields = beanClass.getDeclaredFields();
        for (Field field : declaredFields) {
            field.setAccessible(true);
            String key = field.getName();
            Object value = null;
            try {
                value = field.get(target);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
            map.put(key, value);
        }
        return map;
    }

五、结果展示

Java实现在线模版预览和下载_第2张图片

你可能感兴趣的:(后端技术杂述)