Java导出Word踩坑实录

项目中遇到简历导出成Word文档,利用poi和freemark都能实现, 但是poi对于循环处理一些数据会有难度,最终采用freemarker实现

准备工作

maven 添加依赖

<!-- poi -->
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi</artifactId>
    <version>3.17</version>
</dependency>
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-scratchpad</artifactId>
    <version>3.17</version>
</dependency>
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>3.17</version>
</dependency>
<!-- freemarker -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.2</version>
</dependency>

<dependency>
    <groupId>org.testng</groupId>
    <artifactId>testng</artifactId>
    <version>RELEASE</version>
</dependency>

由于是springboot工程,freemarker的jar包要引对,之前引的不对,浪费很多时间

思路

1.创建docx模板样式
2.将docx转成word xml文件
3.将xml文件的变量占位符换成freekmarker的语法
4.java程序通过freemarker技术替换变量,生成word文件
5.前端下载word文件

实现

WordUtils

/**
 * @author mengqa
 * @date 2018-03-29
 **/
public class WordUtils {
    private static Configuration configuration;
    private static String TEMPLATE_PATH = System.getProperty("user.dir") + File.separator + "template" + File.separator;

    static {
        configuration = new Configuration();
        configuration.setDefaultEncoding("utf-8");
        try {
            configuration.setDirectoryForTemplateLoading(new File(TEMPLATE_PATH));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private WordUtils() {
        throw new AssertionError();
    }

    public static void exportMillCertificateWord(HttpServletRequest request, HttpServletResponse response, Map map,
                                                 String title, String ftlFile) throws IOException {
        Template freemarkerTemplate = configuration.getTemplate(ftlFile);
        File file = null;
        InputStream fin = null;
        ServletOutputStream out = null;
        try {
            // 调用工具类的createDoc方法生成Word文档
            file = createDoc(map, freemarkerTemplate);
            fin = new FileInputStream(file);

            response.setCharacterEncoding("utf-8");
            // 设置浏览器以下载的方式处理该文件名
            String fileName = title + new Date().getTime() + ".doc";
            response.setContentType("application/msword");
            response.setHeader("Content-Disposition", "attachment;filename="
                    .concat(String.valueOf(URLEncoder.encode(fileName, "UTF-8"))));

            out = response.getOutputStream();
            byte[] buffer = new byte[512];  // 缓冲区K
            int count = 0;
            while ((count = fin.read(buffer)) > 0) {
                out.write(buffer, 0, count);
            }
        } finally {
            if (fin != null) fin.close();
            if (out != null) out.close();
            if (file != null) file.delete(); // 删除临时文件
        }
    }

    private static File createDoc(Map<?, ?> dataMap, Template template) {
        String name = "sellPlan.doc";
        File f = new File(name);
        Template t = template;
        try {
            Writer w = new OutputStreamWriter(new FileOutputStream(f), "utf-8");
            t.process(dataMap, w);
            w.close();
        } catch (Exception ex) {
            ex.printStackTrace();
            throw new RuntimeException(ex);
        }
        return f;
    }

    /*public static String getImageStr(String imgFile){
        InputStream in=null;
        byte[] data=null;
        try {
            in=new FileInputStream(imgFile);
            data=new byte[in.available()];
            in.read(data);
            in.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        BASE64Encoder encoder=new BASE64Encoder();
        return encoder.encode(data);
    }*/

    public static String getImageFormNet(String urlStr) {
        //new一个URL对象
        URL url = null;
        //打开链接
        HttpURLConnection conn = null;
        try {
            url = new URL(urlStr);
            conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("GET");
            conn.setConnectTimeout(5 * 1000);
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (ProtocolException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        InputStream in;
        byte[] data = null;
        try {
            in = conn.getInputStream();
            data = new byte[in.available()];
            in.read(data);
            in.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        BASE64Encoder encoder = new BASE64Encoder();
        if (data != null) {
            return encoder.encode(data);
        }
        return null;
    }


}

WordController

/**
 * Word处理器
 * @author mengqa
 * @date 2018-03-27
 **/
@RestController
@RequestMapping("/word")
public class WordController {

    @Autowired
    private IResumeService resumeService;

    @Autowired
    private Config config;

    private String getDomain() {
        return config.getDomain();
    }

    @GetMapping("/export/{resumeId}")
    @ApiOperation(value = "根据resumeId导出")
    @ResponseBody
    public void export(HttpServletRequest request, HttpServletResponse response, @PathVariable Long resumeId) throws IOException {
        ZsWarpResume obj = resumeService.getById(resumeId);
        Map<String, Object> dataMap = new HashMap<>();
        // 基本信息
        dataMap.put("name", obj.getName());
        dataMap.put("sex", obj.getSexStr());
        dataMap.put("born", DateFormatUtils.format(obj.getBorn(), DateFormatUtils.ISO_8601_EXTENDED_DATE_FORMAT.getPattern()));
        dataMap.put("email", obj.getEmail());
        dataMap.put("contact", obj.getContact());
        dataMap.put("address", obj.getAddress());
        String head = obj.getHead();
        String imgUrl = getDomain() + head + "?imageMogr2/auto-orient";
        dataMap.put("image", WordUtils.getImageFormNet(imgUrl));
        // 其他信息
        makeStudyList(obj, dataMap);
        WordUtils.exportMillCertificateWord(request, response, dataMap,"简历" + obj.getName(),"resume.ftl");
    }

    private void makeStudyList(ZsWarpResume obj, Map<String, Object> dataMap) {
        List<ZsWrapStudyHistory> studyList = obj.getStudyList();
        List<Map<String, Object>> studyMapList = new ArrayList<>();
        for (ZsWrapStudyHistory studyHistory : studyList) {
            Map<String, Object>  mObj = new HashMap<>();
            mObj.put("name", studyHistory.getName());
            mObj.put("begindate", studyHistory.getBegindate());
            mObj.put("enddate", studyHistory.getEnddate());
            mObj.put("education", studyHistory.getEducation());
            studyMapList.add(mObj);
        }
        dataMap.put("studyList", studyMapList);
    }

}

前端js


function exportWord(id) {
    window.location.href = "/word/export/" + id;
}

坑总结

*.ftl文件,springboot jar方式运行程序, 读取resource只能通过流的形式,导致会读不到ftl文件,所以用思路是用流的形式读出resourc文件,然后复制到外部文件目录。(在springboot项目启动时复制模板文件)

/**
 * 启动方法类
 * @author mengqa
 * @date 2018-03-29
 **/
@Component
public class MyCommandLineRunner implements CommandLineRunner {

    @Override
    public void run(String... var1) {
        // 因为jar运行方式无法读取resource模板文件,必须先创建外部资源文件
        String path = System.getProperty("user.dir") + File.separator + "template" + File.separator;
        File dir = new File(path);
        if (!dir.exists()) {
            dir.mkdirs();
        }
        installTemplate();
    }

    /**
     * 向外部写resume文件
     */
    private static void installTemplate() {
        System.out.println("创建word模板文件");
        String path = System.getProperty("user.dir") + File.separator + "template" + File.separator;
        Resource resource = new ClassPathResource("generate/resume.ftl");
        File targetFile = new File(path + "resume.ftl");
        try {
            FileUtils.copyInputStreamToFile(resource.getInputStream(), targetFile);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

猜你喜欢

转载自blog.csdn.net/zyjcxc/article/details/79752842