admin管理员组

文章数量:1589946

  • **项目背景

    随着项目日新月异的变化,对数据的安全性也越来越高,特别是政府类型的项目,数据安全往往是非常重要的,最近项目中导出的文件被要求需要密码才能打开,所以写下这篇文章,特此记录一下。

文章目录

  • 前言
  • 一、文档
  • 二、思考与实现
    • 1.ModelMap方式导出
    • 2.Workbook方式的导出
    • 3.封装导出工具类:
  • 至此,功能已完成


前言

本项目中使用的poi框架为easypoi,springboot版本为2.0.3

提示:以下是本篇文章正文内容,下面案例可供参考

一、文档

easypoi使用教程1.0

二、思考与实现

1.ModelMap方式导出

代码如下(示例):

  List<ExcelExportEntity> entityList = new ArrayList<>();
        entityList.add(new ExcelExportEntity("乡镇", "town", 15));
        entityList.add(new ExcelExportEntity("社区", "community", 15));
        entityList.add(new ExcelExportEntity("姓名", "name", 15));
        entityList.add(new ExcelExportEntity("手机号", "phone", 15));
        entityList.add(new ExcelExportEntity("身份证号码", "idcard", 15));
        entityList.add(new ExcelExportEntity("健康管理措施", "typeName", 15));
        entityList.add(new ExcelExportEntity("健康管理地址", "addr", 15));
        entityList.add(new ExcelExportEntity("健康管理开始时间", "startTime", 15));
        entityList.add(new ExcelExportEntity("健康管理结束时间", "endTime", 15));
        List<PersonImportant> dataResult = personImportantService.selectExcelList(para);
        modelMap.put(MapExcelConstants.ENTITY_LIST, entityList);
        modelMap.put(MapExcelConstants.MAP_LIST, dataResult);
        modelMap.put(MapExcelConstants.FILE_NAME, "重点人员健康管理人员信息");
        SessionUser sessionUser = ShiroUtils.getSessionUser();
        String password = sessionUser.getExcelPassword();
        if (StringUtils.isEmpty(password)) {
            password = "123456";
        }
        modelMap.put(MapExcelConstants.PASSWORD, password);
        modelMap.put(NormalExcelConstants.PARAMS, new ExportParams("重点人员健康管理人员信息", "重点人员健康管理人员信息", ExcelType.XSSF));
        return MapExcelConstants.EASYPOI_MAP_EXCEL_VIEW;

这种方式的导出,我们看到,官方是自带了文档加密功能,只需要我们通过参数的方式传递过去就好了,所以我们不细讲。

2.Workbook方式的导出

代码如下(示例):


	@KrtLog("下载导入失败数据")
    @RequiresPermissions("person:personImportantDetail:excelIn")
    @GetMapping("person/personImportant/downloadExcelErrorData")
    public void downloadExcelErrorData(String downErrorDataKey) {

        List<PersonImportantDetail> dataResult = personImportantService.getExcelErrorData(downErrorDataKey);
        Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams("重点人员健康管理人员信息", "重点人员健康管理人员信息", ExcelType.XSSF), PersonImportantDetail.class, dataResult);
        ExportExcelUtil.exportExcel(workbook, "导入失败-重点人员健康管理人员信息", response);
    }

这种方式的导出,查阅了官方文档,没看到官方的加密功能。通过阅读ModelMap方式导出的源码发现了其加密的奥秘:

通过代码我们可以发现:

ModelMap方式的导出最后是会到这个控制层,而通过源码我们可以看到,最终是在这个controller中进行导出的:

那么让我们看看这个里面究竟做了些什么事情吧!继续追踪源码,我们可以发现,具体的实现都是在一个叫out()的方法中进行的:

让我们再次深入其中,为了更好的展现,我把这部分的源码单独粘贴出来了:

 public void out(Workbook workbook, String codedFileName, String password, HttpServletRequest request, HttpServletResponse response) throws Exception {
        if (workbook instanceof HSSFWorkbook) {
            codedFileName = codedFileName + ".xls";
        } else {
            codedFileName = codedFileName + ".xlsx";
        }

        if (password != null && !"".equals(password)) {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            workbook.write(baos);
            baos.flush();
            ByteArrayInputStream workbookInput = new ByteArrayInputStream(baos.toByteArray());
            EncryptionInfo info = new EncryptionInfo(EncryptionMode.agile);
            Encryptor enc = info.getEncryptor();
            enc.confirmPassword(password);
            POIFSFileSystem fs = new POIFSFileSystem();
            OPCPackage opc = OPCPackage.open(workbookInput);
            OutputStream os = enc.getDataStream(fs);
            opc.save(os);
            os.close();
            opc.close();
            baos = new ByteArrayOutputStream();
            fs.writeFilesystem(baos);
            baos.flush();
            response.setHeader("content-disposition", WebFilenameUtils.disposition(codedFileName));
            byte[] buff = new byte[1024];
            BufferedInputStream bis = null;

            try {
                OutputStream os = response.getOutputStream();
                bis = new BufferedInputStream(new ByteArrayInputStream(baos.toByteArray()));

                for(int i = bis.read(buff); i != -1; i = bis.read(buff)) {
                    os.write(buff, 0, buff.length);
                    os.flush();
                }
            } catch (IOException var24) {
                var24.printStackTrace();
            } finally {
                if (bis != null) {
                    try {
                        bis.close();
                    } catch (IOException var23) {
                        var23.printStackTrace();
                    }
                }

            }
        } else {
            response.setHeader("content-disposition", WebFilenameUtils.disposition(codedFileName));
            ServletOutputStream out = response.getOutputStream();
            workbook.write(out);
            out.flush();
        }

    }

至此,我们便发现了他的庐山真面目,我们可以参照他的源码,自己写一个导出工具类。

3.封装导出工具类:

package com.krt.common.util;

import lombok.extern.slf4j.Slf4j;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.poifs.crypt.EncryptionInfo;
import org.apache.poi.poifs.crypt.EncryptionMode;
import org.apache.poi.poifs.crypt.Encryptor;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.ss.usermodel.Workbook;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;

/**
 * @author: lyp
 * @Date: 2022/1/24 10:38
 * @Description: excel导出工具类
 */
@Slf4j
public class ExportExcelUtil {
    /**
     * 导出Excel
     *
     * @param workbook workbook流
     * @param fileName 文件名
     * @param response 响应
     */
    public static void exportExcel(Workbook workbook, String fileName, HttpServletResponse response) {
        // 输出文件
        try (OutputStream out = response.getOutputStream()) {
            // 获取文件名并转码
            String name = URLEncoder.encode(fileName, "UTF-8");
            // 编码
            response.setCharacterEncoding("UTF-8");
            // 设置强制下载不打开
            response.setContentType("application/force-download");
            // 下载文件的默认名称
            response.setHeader("Content-Disposition", "attachment;filename=" + name + ".xlsx");
            // 输出表格
            workbook.write(out);
        } catch (IOException e) {
            log.error("文件导出异常,详情如下:{}", e);
        } finally {
            try {
                if (workbook != null) {
                    // 关闭输出流
                    workbook.close();
                }
            } catch (IOException e) {
                log.error("文件导出关闭输出流失败,详情如下:{}", e);
            }
        }
    }


    /**
     * 导出一个需要密码打开的Excel
     *
     * @param workbook workbook流
     * @param fileName 文件名
     * @param response 响应
     */
    public static void exportEncryptExcel(Workbook workbook, String fileName, HttpServletResponse response, String password) throws Exception {
        //
        if (password != null && !"".equals(password)) {
            // 文件名
            fileName = fileName + ".xlsx";

            // 创建一个字节数组输出流
            ByteArrayOutputStream workbookOutput = new ByteArrayOutputStream();
            workbook.write(workbookOutput);
            workbookOutput.flush();

            // 创建一个字节数组输入流
            ByteArrayInputStream workbookInput = new ByteArrayInputStream(workbookOutput.toByteArray());

            // 加密
            EncryptionInfo info = new EncryptionInfo(EncryptionMode.agile);
            Encryptor enc = info.getEncryptor();
            enc.confirmPassword(password);

            // 创建一个POIFS 文件系统
            POIFSFileSystem poifsFileSystem = new POIFSFileSystem();
            OPCPackage opc = OPCPackage.open(workbookInput);
            OutputStream outputStream = enc.getDataStream(poifsFileSystem);
            opc.save(outputStream);
            outputStream.close();
            opc.close();
            workbookOutput = new ByteArrayOutputStream();
            poifsFileSystem.writeFilesystem(workbookOutput);
            workbookOutput.flush();

            // 获取文件名并转码
            String name = URLEncoder.encode(fileName, "UTF-8");
            // 编码
            response.setCharacterEncoding("UTF-8");
            // 设置强制下载不打开
            response.setContentType("application/force-download");
            // 下载文件的默认名称
            response.setHeader("Content-Disposition", "attachment;filename=" + name);
            byte[] buff = new byte[1024];
            BufferedInputStream bufferedInputStream = null;

            try {
                OutputStream responseOutputStream = response.getOutputStream();
                bufferedInputStream = new BufferedInputStream(new ByteArrayInputStream(workbookOutput.toByteArray()));

                for (int i = bufferedInputStream.read(buff); i != -1; i = bufferedInputStream.read(buff)) {
                    responseOutputStream.write(buff, 0, buff.length);
                    responseOutputStream.flush();
                }
            } catch (IOException e) {
                log.error("文件导出失败,详情如下:{}", e);
            } finally {
                if (bufferedInputStream != null) {
                    try {
                        bufferedInputStream.close();
                    } catch (IOException e) {
                        log.error("文件导出关闭输出流失败,详情如下:{}", e);
                    }
                }

            }
        }

    }

}

至此,功能已完成

本文标签: 文档JavaeasypoiExcel