EasyPoi word导出教程

(不需要copy代码,请专注于理解如何实现,我会详细解释代码!)

一、简要描述
  One day,我参与的项目中,用户提了个新需求。要求:按照给定 word模板,填充数据并导出。

  负责这个需求后,我查阅CSDN,虽然也找到了一些文章,依据文章中的顺序,写出了demo。但demo毕竟是demo,在项目实战中,还是让我遇到不少坑。

  由于 easypoi对Word的支持不太友好,不支持多图片导出,因此研究之后,也找到一个办法,后续会展开叙述。

二、项目描述
  主要涉及:SpringBoot + MySQL + Linux

  实际项目会部署在Linux上,图片也是需要从服务器上解析到本地,转成Base64位字节才可以。不过我会提供全部的图片转Base64公共方法。

注意理解:Word支持显示的图片,是图片通过IO流转成的Base64字节

(正式开始 ===> follow me!)

第一步:准备Word模板
EasyPoi word导出教程_第1张图片

第二步:导入Jar包

		<!-- 集成 easypoi -->
        <dependency>
            <groupId>cn.afterturn</groupId>
            <artifactId>easypoi-base</artifactId>
            <version>3.3.0</version>
        </dependency>
        <dependency>
            <groupId>cn.afterturn</groupId>
            <artifactId>easypoi-web</artifactId>
            <version>3.3.0</version>
        </dependency>
        <dependency>
            <groupId>cn.afterturn</groupId>
            <artifactId>easypoi-annotation</artifactId>
            <version>3.3.0</version>
        </dependency>

第三步:Controller层 接口

	@ApiOperation(value = "导出巡查整改通知单")
    @PostMapping("/letter/notice/export")
    @ApiImplicitParams({@ApiImplicitParam(name = "id", value = "巡查id")})
    public void wordExport(@RequestParam Long id,HttpServletRequest request, HttpServletResponse response) throws IcmsException, IOException {
    	// 注入Service层
        service.wordExport(id, request, response);
    }

第四步:Service层 业务

	@Override
    public void wordExport(Long id, HttpServletRequest request, HttpServletResponse response) throws IcmsException, IOException {
        // 1.查询数据
        User user = userDao.selectList(new QueryWrapper<User>().eq("id",id));

        // 2.创建 map
        Map<String,Object> data = new HashMap<>();
        data.put("number",new SimpleDateFormat("yyyyMMddHHmmss").format(new Date())); // 通知单号
        data.put("unitName", StringUtils.isEmpty(user.getUnitName()) ? " ":user.getUnitName()); // 单位
        data.put("sendTime",null == user.getSendTime() ? " ":new SimpleDateFormat("yyyy年MM月dd日HH时mm分ss秒").format(user.getSendTime()));
        data.put("type",StringUtils.isEmpty(user.getType()) ? " ":user.getType()); // 问题类型
        data.put("content",StringUtils.isEmpty(user.getContent()) ? " ":user.getContent()); // 问题描述

        // 3.处理图片
        String httpPath = request.getScheme() + "://" + request.getServerName() + user.getUrl();  // 图片路径
        WordImageEntity image = new WordImageEntity(FileUtils.getImageBase64(httpPath),384,216);
        image.setType(WordImageEntity.Data); // 设置成 Byte字节
        data.put("image",image);

        // 4.导出
        FileUtils.easyPoiExport("/opt/baidu/web/file/word/template.docx","tempDir","test.docx",data,request,response);
    }

描述:
1、进入Service层,第一步是查询本次需要导出的数据。
2、创建map的作用:easypoi会通过map的key值,映射到word模板上,替换value值。

重要
真正在写业务的时候,请注意对替换的值,进行判空处理!如果值为空,请使用“ ”,而不是“”。中间要空格!

图片
代码中的 String httpPath = “”,由于我的项目部署在Linux服务器上,图片也在服务器中,需要获取全路径。Windows系统本地开发,可使用本地图片全路径。

String httpPath = request.getScheme() + "://" + request.getServerName() + file.getUrl();

输出结果:http://172.16.231.188/file/img/2020-03-29/161699762599480.jpg

WordImageEntity 详解
  1、WordImageEntity类提供了2个构造方法,一个可直接传图片URl,一个传图片字节byte[]。此处建议使用public WordImageEntity(byte[] data) --缩写构造方法。

  2、WordImageEntity类支持设置图片宽度、高度。
EasyPoi word导出教程_第2张图片
关于FileUtils.getImageBase64()这个方法继续往下看会讲到。

第五步:FileUtils

通用工具类
public class FileUtils {

    /**
     * EasyPoi 替换数据 导出 word
     * @param templatePath word模板地址
     * @param tempDir      临时文件存放地址
     * @param filename     文件名称
     * @param data         替换参数
     * @param request
     * @param response
     */
    public static void easyPoiExport(String templatePath, String tempDir, String filename, Map<String, Object> data, HttpServletRequest request, HttpServletResponse response) {
        Assert.notNull(templatePath, "模板路径不能为空");
        Assert.notNull(tempDir, "临时文件路径不能为空");
        Assert.notNull(filename, "文件名称不能为空");
        Assert.isTrue(filename.endsWith(".docx"), "文件名称仅支持docx格式");

        if (!tempDir.endsWith("/")) {
            tempDir = tempDir + File.separator;
        }

        File file = new File(tempDir);
        if (!file.exists()) {
            file.mkdirs();
        }

        try {
            String userAgent = request.getHeader("user-agent").toLowerCase();
            if (userAgent.contains("msie") || userAgent.contains("like gecko")) {
                filename = URLEncoder.encode(filename, "UTF-8");
            } else {
                filename = new String(filename.getBytes("utf-8"), "ISO-8859-1");
            }

            XWPFDocument document = WordExportUtil.exportWord07(templatePath, data);
            String tempPath = tempDir + filename;
            FileOutputStream out = new FileOutputStream(tempPath);
            document.write(out);

            // 设置响应规则
            response.setContentType("application/force-download");
            response.addHeader("Content-Disposition", "attachment;filename=" + filename);
            OutputStream stream = response.getOutputStream();
            document.write(stream);
            stream.close();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            deleteTempFile(tempDir, filename);
        }
    }

    /**
     * 删除临时生成的文件
     */
    public static void deleteTempFile(String filePath, String fileName) {
        File file = new File(filePath + fileName);
        File f = new File(filePath);
        file.delete();
        f.delete();
    }
}

至此,按照以上顺序,即可完成 easypoi 导出 word!

三、扩展

1、图片转字节数组(Linux生产环境、本地开发环境)

	/**
     * Linux生产环境 将图片转换为字节数组
     * @param path 文件 http路径
     * @return base64编码过的字节数组
     * @throws IOException
     */
    public static byte[] getImageBase64(String path) throws IOException {
        byte[] data = null;
        URL url = null;
        InputStream input = null;
        try{
            url = new URL(path);
            HttpURLConnection httpUrl = (HttpURLConnection) url.openConnection();
            httpUrl.connect();
            httpUrl.getInputStream();
            input = httpUrl.getInputStream();
        }catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        ByteArrayOutputStream output = new ByteArrayOutputStream();
        byte[] buf = new byte[1024];
        int numBytesRead = 0;
        while ((numBytesRead = input.read(buf)) != -1) {
            output.write(buf, 0, numBytesRead);
        }
        data = output.toByteArray();
        output.close();
        input.close();
        return data;
    }
	/**
     * 本地开发环境 将图片转换为字节数组存储
     * @param path 文件 http路径
     * @return base64编码过的字节数组
     * @throws IOException
     */
    public static byte[] getImageBase64(String path) throws IOException {
        InputStream input = new FileInputStream(path);
        ByteArrayOutputStream output = new ByteArrayOutputStream();
        byte[] buf = new byte[1024];
        int numBytesRead = 0;
        while ((numBytesRead = input.read(buf)) != -1) {
            output.write(buf, 0, numBytesRead);
        }
        byte[] data = output.toByteArray();
        output.close();
        input.close();
        return data;
    }

2、多图片导出(easypoi 并不支持多图片导出,以下仅供参考!)

(1)修改模板
EasyPoi word导出教程_第3张图片
由于 easypoi并不支持多图片导出,因此该模板与原先模板不同之处就在于,在显示图片的地方,多定义标签,并设置下标。

(2)业务层代码

	// 1.全部图片信息 files
	List<FileInfoModel> files = detail.getFiles();
	if(null != files && files.size() > 0){ 
		List<WordImageEntity> images = new ArrayList<>();
			for (FileInfoModel file : files) {
				// 2.处理每一张图片,并将其添至 images集合中
				String httpPath = request.getScheme() + "://" + request.getServerName() + file.getUrl();  // 图片路径
				WordImageEntity image = new WordImageEntity(FileUtils.getImageBase64(httpPath),384,216);
				image.setType(WordImageEntity.Data);
                    
				images.add(image);
                }
                
                // 3.图片赋值
                for (int i = 0; i < images.size(); i++) {
                    data.put("image_"+i, images.get(i));
                }
                
                // 4.多余 {{image_x}} 标签要赋值为空
                for (int i = images.size(); i < 5; i++) {
                    data.put("image_"+i, " ");
                }
            }else{
                // 5.如果没有图片,所有图片标签置空
                for (int i = 0; i < 5; i++) {
                    data.put("image_"+i, " ");
                }
            }

如果你对于 easypoi所实现的效果不满意,可以选择使用 freemarker技术来生成 word文档。
为此我也撰写了一份博客:Freemarker word 导出

至此,完結~

你可能感兴趣的:(java,poi)