天天看點

Java實作線上模版預覽和下載下傳一、基礎準備二、根據使用者輸入填充ftl生成word文檔三、word文檔轉化為HTML實作線上預覽 四、解決Ftl模闆生成doc文檔的不足五、結果展示

*** 所有目錄參數皆為項目相對路徑

一、基礎準備

        1.1 Maven建構項目

<dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.7</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-io</artifactId>
            <version>LATEST</version>
        </dependency>
        <dependency>
            <groupId>fr.opensagres.xdocreport</groupId>
            <artifactId>fr.opensagres.xdocreport.document</artifactId>
            <version>1.0.5</version>
        </dependency>
        <dependency>
            <groupId>fr.opensagres.xdocreport</groupId>
            <artifactId>org.apache.poi.xwpf.converter.xhtml</artifactId>
            <version>1.0.5</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-scratchpad</artifactId>
            <version>3.12</version>
        </dependency>
           

        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<File> getFiles(String targetPath) {
        String realPath = getRealPath(targetPath);
        List<File> 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實作線上模版預覽和下載下傳一、基礎準備二、根據使用者輸入填充ftl生成word文檔三、word文檔轉化為HTML實作線上預覽 四、解決Ftl模闆生成doc文檔的不足五、結果展示

        1.5 擷取填充資料

/**
     * 注意dataMap裡存放的資料Key值要與模闆中的參數相對應
     *
     * @param target
     */
    private static Map<String, Object> getData(Object target) {
        Map<String, Object> 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<String, Object> 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<String ,Object> 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<String, Object> params) {
        Iterator<XWPFParagraph> 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<String, Object> params) {
        List<XWPFRun> 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<Picture> 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<String, Object> getData(Object target) {
        if (target instanceof Map) {
            return (Map<String, Object>) target;
        }

        Map<String, Object> 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實作線上模版預覽和下載下傳一、基礎準備二、根據使用者輸入填充ftl生成word文檔三、word文檔轉化為HTML實作線上預覽 四、解決Ftl模闆生成doc文檔的不足五、結果展示