Java导出docx文件

docx

docx是Microsoft Office2007之后版本使用的,用新的基于XML的压缩文件格式取代了其目前专有的默认文件格式,在传统的文件名扩展名后面添加了字母“x”(即“.docx”取代“.doc”、“.xlsx”取代“.xls”、“.pptx”取代“.ppt”)。

如何做导出

docx是一个压缩文件,所以要导出docx就必须要先进行解压,然后进行模板替换。再压缩为docx。


image.png

目录结构如下:


TIM图片20190320123606.png

踩坑记录:
导入的图片后缀必须为jpeg

代码实现

压缩解压代码

package com.hengyi.docxdemo;

import java.io.*;
import java.nio.charset.Charset;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;

/**
 *
 * @author dongzp
 * @date 2019-03-20
 * @version 1.0
 */
public class ZipUtils {

    /**
     * 使用GBK编码可以避免压缩中文文件名乱码
     */
    private static final String CHINESE_CHARSET = "GBK";

    /**
     * 文件读取缓冲区大小
     */
    private static final int CACHE_SIZE = 1024;

    /**
     * <p>
     * 压缩文件
     * </p>
     *
     * @param sourceFolder 压缩文件夹
     * @param zipFilePath 压缩文件输出路径
     * @throws Exception
     */
    public static void zip(String sourceFolder, String zipFilePath) throws Exception {
        OutputStream out = new FileOutputStream(zipFilePath);
        BufferedOutputStream bos = new BufferedOutputStream(out);
        ZipOutputStream zos = new ZipOutputStream(bos);
        // 解决中文文件名乱码
//        zos.setEncoding(CHINESE_CHARSET);
        File file = new File(sourceFolder);
        String basePath = null;
        if (file.isDirectory()) {
            basePath = file.getPath();
        } else {
            basePath = file.getParent();
        }
        zipFile(file, basePath, zos);
        zos.closeEntry();
        zos.close();
        bos.close();
        out.close();
    }

    /**
     * <p>
     * 递归压缩文件
     * </p>
     *
     * @param parentFile
     * @param basePath
     * @param zos
     * @throws Exception
     */
    private static void zipFile(File parentFile, String basePath, ZipOutputStream zos) throws Exception {
        File[] files;
        if (parentFile.isDirectory()) {
            files = parentFile.listFiles();
        } else {
            files = new File[1];
            files[0] = parentFile;
        }
        String pathName;
        InputStream is;
        BufferedInputStream bis;
        byte[] cache = new byte[CACHE_SIZE];
        for (File file : files) {
            if (file.isDirectory()) {
                zipFile(file, basePath, zos);
            } else {
                pathName = file.getPath().substring(basePath.length() + 1);
                is = new FileInputStream(file);
                bis = new BufferedInputStream(is);
                zos.putNextEntry(new ZipEntry(pathName));
                int nRead = 0;
                while ((nRead = bis.read(cache, 0, CACHE_SIZE)) != -1) {
                    zos.write(cache, 0, nRead);
                }
                bis.close();
                is.close();
            }
        }
    }

    /**
     * <p>
     * 解压压缩包
     * </p>
     *
     * @param zipFilePath 压缩文件路径
     * @param destDir 压缩包释放目录
     * @throws Exception
     */
    /**
     * 解压文件
     * @param zipPath 要解压的目标文件
     * @param descDir 指定解压目录
     * @return 解压结果:成功,失败
     */
    public static boolean unZip(String zipPath, String descDir) {
        File zipFile = new File(zipPath);
        boolean flag = false;
        File pathFile = new File(descDir);
        if(!pathFile.exists()){
            pathFile.mkdirs();
        }
        ZipFile zip = null;
        try {
            zip = new ZipFile(zipFile, Charset.forName("gbk"));//防止中文目录,乱码
            for(Enumeration entries = zip.entries(); entries.hasMoreElements();){
                ZipEntry entry = (ZipEntry)entries.nextElement();
                String zipEntryName = entry.getName();
                InputStream in = zip.getInputStream(entry);
                //指定解压后的文件夹+当前zip文件的名称
                String outPath = (descDir+zipEntryName).replace("/", File.separator);
                //判断路径是否存在,不存在则创建文件路径
                File file = new File(outPath.substring(0, outPath.lastIndexOf(File.separator)));
                if(!file.exists()){
                    file.mkdirs();
                }
                //判断文件全路径是否为文件夹,如果是上面已经上传,不需要解压
                if(new File(outPath).isDirectory()){
                    continue;
                }
                //保存文件路径信息(可利用md5.zip名称的唯一性,来判断是否已经解压)
                System.err.println("当前zip解压之后的路径为:" + outPath);
                OutputStream out = new FileOutputStream(outPath);
                byte[] buf1 = new byte[2048];
                int len;
                while((len=in.read(buf1))>0){
                    out.write(buf1,0,len);
                }
                in.close();
                out.close();
            }
            flag = true;
            zip.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return flag;
    }

    public static void main(String[] args){
        //1.压缩一个文件
        try {
            unZip("E:\\zzzz.zip","E:\\zzzz\\");
            System.out.println("压缩完成");
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

最终实现

/**
 * Created:2019/3/20
 * Time:9:32
 * Author:dongzp
 * Email:90fanhua@gmail.com
 * Project:docx-demo
 * Use:
 */
public class WordDocxExportUtil {

    /*
     * 实现文件的拷贝
     * @param srcPathStr
     *          源文件的地址信息
     * @param desPathStr
     *          目标文件的地址信息
     */
    private static String copyFile(String srcPathStr, String desPath,String newFileName) {
        newFileName = "image" + newFileName + ".jpeg";
        desPath = desPath + File.separator + newFileName;
        try{
            //2.创建输入输出流对象
            FileInputStream fis = new FileInputStream(srcPathStr);
            FileOutputStream fos = new FileOutputStream(desPath);

            //创建搬运工具
            byte datas[] = new byte[1024*8];
            //创建长度
            int len = 0;
            //循环读取数据
            while((len = fis.read(datas))!=-1){
                fos.write(datas,0,len);
            }
            //3.释放资源
            fis.close();
            fos.close();
            return newFileName;
        }catch (Exception e){
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 清空媒体文件
     * @param media
     */
    private static void clearMediaFile(File media){
        System.out.println("meida:" + media.isDirectory());
        if(media.isDirectory()){
            File[] fileList = media.listFiles();
            if(fileList != null){
                for(File file : fileList)
                    if(file.exists())
                        file.delete();
            }
        }
    }


    /**
     * 模板替换
     * @param templateFile
     * @param distFile
     */
    private static void templateReplace(String templateDir,String templateFile,String distFile,Map<String,Object> data)throws Exception{
        Configuration config = new Configuration();
        config.setDirectoryForTemplateLoading(new File(templateDir));
        config.setObjectWrapper(new DefaultObjectWrapper());
        Template template = config.getTemplate(templateFile, "UTF-8");

        FileOutputStream fos = new FileOutputStream(distFile);
        Writer out = new OutputStreamWriter(fos, "UTF-8");
        template.process(data, out);
        out.flush();
        out.close();
    }

    /**
     * 导出Docx
     * @param outFile           导出的目录
     * @param template          模板文件根目录
     * @param documentFtl       document模板文件
     * @param documentRelsFtl   documentRels模板文件
     * @param data              document数据
     * @param images            documentRles数据
     * @return
     * @throws Exception
     */
    public static  void exportDocx(File outFile,File template,File documentFtl,File documentRelsFtl,Map<String,Object> data,List<Map<String,String>> images, HttpServletResponse response)throws Exception{
        /**获取document.xml文件路径*/
        File document = new File(template.getAbsolutePath() + File.separator + "word","document.xml");
        System.out.println("获取document.xml:" + document.getAbsolutePath());

        /**获取document.xml.rels文件路径*/
        File documentRels = new File(template.getAbsolutePath() + File.separator + "word" + File.separator + "_rels","document.xml.rels");
        System.out.println("获取document.xml.rels:" + documentRels.getAbsolutePath());

        /**获取media目录*/
        File mediaDir = new File(template.getAbsolutePath() + File.separator + "word" + File.separator + "media" + File.separator);
        if(!mediaDir.exists())
            throw new RuntimeException("media不存在");
        // TODO: 2019/3/20 清空media 模板
        clearMediaFile(mediaDir);

        templateReplace(documentFtl.getParent(),documentFtl.getName(),document.getAbsolutePath(),data);

        for(Map<String,String> image : images){
            String newFileName = copyFile(image.get("image"),mediaDir.getAbsolutePath(),image.get("index"));
            if(newFileName == null)
                image.put("image","image.jpeg");
            else
                image.put("image",newFileName);
        }

        Map<String,Object> rels = new HashMap<>();
        rels.put("relsList",images);

        templateReplace(documentRelsFtl.getParent(),"document.xml.rels.ftl",documentRels.getAbsolutePath(),rels);

        /**压缩模板文件生成docx*/
        ZipUtils.zip(template.getAbsolutePath(),outFile.getAbsolutePath());


        /**导出*/
        InputStream is = new FileInputStream(outFile);
        response.reset();
        response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document;charset=UTF-8");
        String docFileName = outFile.getName();
        docFileName = new String(docFileName.getBytes("utf-8"),"iso-8859-1");
        response.addHeader("Content-Disposition", "attachment;filename="+docFileName);

        byte[] b = new byte[1024];
        int len;
        while ((len=is.read(b)) >0) {
            response.getOutputStream().write(b,0,len);
        }
        response.getOutputStream().flush();
        response.getOutputStream().close();
    }
}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,884评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,755评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,369评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,799评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,910评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,096评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,159评论 3 411
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,917评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,360评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,673评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,814评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,509评论 4 334
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,156评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,882评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,123评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,641评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,728评论 2 351