POI实战——好久不见,导出Excel的方式你知道几种?
阅读原文时间:2021年04月22日阅读:1

文章目录


常用的几种方式:

①hutool工具(底层POI)

②模板引擎(thymeleaf或FreeMarker)

③POI

④其他对POI封装的插件

总的来说,POI好强大,不会用岂不可惜?继续往下看我使用POI批量导出Excel的实战日志吧!


一、POI简介

Apache POI api的主要用途是用于文本提取应用程序,例如网络蜘蛛,索引构建器和内容管理系统。
官网如是说,其实就是用来读写Excel、Word文档、PPT等常见文档的工具而已。官网有详细说明,本贴重点在于拿来就用,省去大部分钻研时间(因为这次是因为开发批量导出Excel,所以只有批量导出Excel的内容,其他内容等我有业务需求再说,哈哈)

二、使用poi的原因,以及为什么出现了本贴

最近公司的一个老项目要添加批量导出功能,首先想到使用hutool,但是需要导出的Excel比较复杂,使用模板引擎(thymeleaf或FreeMarker)导出是比较省事的,但是公司的项目使用的是JSP。。。
所以只好使用POI啦。
思路:导出Excel文件,文件内包含多个sheet即多张表。
因为项目没有使用maven,所以直接导的jar包
使用maven的小伙伴或者需要导jar包的,可以去 https://mvnrepository.com/search?q=poi 下载

三、依赖的包

我需要导出Excel,所以下边三个就够了

如果你有操作其他文档的需求,但是不知道需要导什么jar包,我给你去官网截个图(链接http://poi.apache.org/components/index.html 浏览器打开 Ctrl+F搜索 Component Map)

看不懂没关系,我给你翻译一下

四、几个单词含义

导出Excel首先要会用Excel不是?起码这几个单词啥意思需要懂

  1. row 行
  2. cell 单元格
  3. sheet 工作表

五、导出方式(创建并导出)

导出到本地磁盘或者浏览器呗,代码在下,毕竟得宠着。看完下边你就知道怎么导出创建的文件了,什么文件导出都是一样。

1、导出到本地磁盘:

代码如下(示例):

/*创建Excel*/
 String filePath="F:\\sample.xls";//文件路径
 HSSFWorkbook workbook = new HSSFWorkbook();//创建Excel文件(Workbook)
 HSSFSheet sheet = workbook.createSheet();//创建工作表(Sheet)
 sheet = workbook.createSheet("Test");//创建工作表(Sheet)
 FileOutputStream out = null;
 try {
     out = new FileOutputStream(filePath);
     workbook.write(out);//保存Excel文件
     out.close();//关闭文件流
 } catch (FileNotFoundException e) {
     e.printStackTrace();
 } catch (IOException e) {
     e.printStackTrace();
 } finally {
     System.out.println("OK!");
 }

2、导出到浏览器下载:

代码如下(示例):

/*创建Excel*/
HSSFWorkbook workbook = new HSSFWorkbook();//创建Excel文件(Workbook)
HSSFSheet sheet = workbook.createSheet();//创建工作表(Sheet)
sheet = workbook.createSheet("Test");//创建工作表(Sheet)
//将文件存到浏览器设置的下载位置
response.reset();
response.setContentType("application/msexcel;charset=UTF-8");
try {
    SimpleDateFormat newsdf=new SimpleDateFormat("yyyyMMddHHmmss");
    String date = newsdf.format(new Date());
    response.addHeader("Content-Disposition", "attachment;filename=\""
            + new String(("数据表" + date + ".xls").getBytes("GBK"),
            "ISO8859_1") + "\"");
    OutputStream out = response.getOutputStream();
    workbook.write(out);
    out.flush();
    out.close();
} catch (FileNotFoundException e) {
    e.printStackTrace();
} catch (IOException e) {
    e.printStackTrace();
}

js:

window.open("/contract/batchExport" ,'_self');
或者
window.location.href="/contract/batchExport";

嗯哼,数据量比较大的参数数据或者post携带的话?使用仿post提交!抱歉我没用上,所以没写,思路是
创建一次性表格,懂了吧

平时使用简单的get携带即可
window.location.href="/contract/batchExport?code=" + code;

3、实战整理后的代码

@RequestMapping("/batchExport")
public void batchExport(HttpServletResponse response, String ids) {
        String[] idList = ids.split(",");
        /*创建Excel*/
        HSSFWorkbook workbook = new HSSFWorkbook();//创建Excel文件(Workbook)

        workbook.createInformationProperties();//创建文档信息
        DocumentSummaryInformation dsi = workbook.getDocumentSummaryInformation();//摘要信息
        dsi.setCategory("Excel文件");//类别
        dsi.setManager("");//管理者
        dsi.setCompany("");//公司
        SummaryInformation si = workbook.getSummaryInformation();//摘要信息
//        si.setSubject("");//主题
        si.setTitle("");//标题
        si.setAuthor("");//作者
//            si.setComments("");//备注
        try {

            for (int i = 0, num = idList.length; i < num; i++) {
                //获取数据
                List<Product> list = contractService.listProduct(idList[i]);
                Contract contract = contractService.getContract(idList[i]);

                if (null!=contract) {
                    HSSFSheet sheet = workbook.createSheet(idList[i]);//创建工作表(Sheet)
                    HSSFRow row = sheet.createRow(0);// 创建行,从0开始
                    //合并列
                    HSSFCell cell = row.createCell(0);
                    cell.setCellValue("标题");
                    CellRangeAddress region1 = new CellRangeAddress(0, 2, 0, 7);
                    sheet.addMergedRegion(region1);

                    HSSFCellStyle style = workbook.createCellStyle();
                    style.setBorderBottom(BorderStyle.THIN);//下边框
                    style.setBorderLeft(BorderStyle.THIN);//左边框
                    style.setBorderRight(BorderStyle.THIN);//右边框
                    style.setBorderTop(BorderStyle.THIN); //上边框
                    HSSFFont font = workbook.createFont();
                    font.setFontHeightInPoints((short) 10);//设置字号
//                    font.setBold(true); //加粗
                    style.setFont(font);
                    style.setWrapText(true);//換行

                    Integer l = list.size();
                    for (int j = 0; j < l; j++) {
                        Product product = list.get(j);
                        HSSFRow rows = sheet.createRow(8 + j);
                        HSSFCell cellj1 = rows.createCell(0);
                        cellj1.setCellValue(product.getProduct_name());
                        HSSFCell cellj2 = rows.createCell(1);
                        cellj2.setCellValue(product.getProduct_model());
                        HSSFCell cellj3 = rows.createCell(2);
                        cellj3.setCellValue(product.getProduct_specification());
                        HSSFCell cellj4 = rows.createCell(3);
                        cellj4.setCellValue(product.getProduct_unit());
                        HSSFCell cellj5 = rows.createCell(4);
                        cellj5.setCellValue(product.getProduct_num());
                        HSSFCell cellj6 = rows.createCell(5);
                        cellj6.setCellValue(product.getProduct_unit_price() + "元");
                        HSSFCell cellj7 = rows.createCell(6);
                        cellj7.setCellValue(product.getMoney() + "元");
                        HSSFCell cellj8 = rows.createCell(7);
                        cellj8.setCellValue(product.getRemark());

                        cellj1.setCellStyle(style);
                        cellj2.setCellStyle(style);
                        cellj3.setCellStyle(style);
                        cellj4.setCellStyle(style);
                        cellj5.setCellStyle(style);
                        cellj6.setCellStyle(style);
                        cellj7.setCellStyle(style);
                        cellj8.setCellStyle(style);
                    }
                    //轮到8+l了
                    HSSFRow row8L = sheet.createRow(8 + l);
                    HSSFCell cell8L_1 = row8L.createCell(0);
                    cell8L_1.setCellValue("合同人民币大写金额:" + NumberToCN.number2CNMontrayUnit(new BigDecimal(contract.getContract_price())));
                    CellRangeAddress region2 = new CellRangeAddress(8 + l, 8 + l, 0, 5);
                    sheet.addMergedRegion(region2);
                    HSSFCell cell8L_2 = row8L.createCell(6);
                    cell8L_2.setCellValue(contract.getContract_price() + "元");
                    CellRangeAddress region3 = new CellRangeAddress(8 + l, 8 + l, 6, 7);
                    sheet.addMergedRegion(region3);
                    cell8L_1.setCellStyle(style);
                    cell8L_2.setCellStyle(style);

                    setRegionBorder(BorderStyle.THIN,region1,sheet);
                    setRegionBorder(BorderStyle.THIN,region2,sheet);
                    setRegionBorder(BorderStyle.THIN,region3,sheet);

                    sheet.setColumnWidth(0, 20 * 256);//设置第一列的宽度是20个字符宽度
                    sheet.setColumnWidth(1, 13 * 256);//设置第二列的宽度是13个字符宽度
                    sheet.setColumnWidth(2, 13 * 256);
                    sheet.setColumnWidth(3, 8 * 256);
                    sheet.setColumnWidth(4, 8 * 256);
                    sheet.setColumnWidth(5, 13 * 256);
                    sheet.setColumnWidth(6, 13 * 256);
                    sheet.setColumnWidth(7, 13 * 256);
                    row.setHeightInPoints(50);//设置行的高度是50个点
                    row8L.setHeightInPoints(20);//设置行的高度是20个点

                    HSSFPrintSetup ps = sheet.getPrintSetup();
                    ps.setLandscape(false); // 打印方向,true:横向,false:纵向
                    ps.setPaperSize(HSSFPrintSetup.A4_PAPERSIZE); //纸张
                    sheet.setMargin(HSSFSheet.BottomMargin, (double) 0.5);// 页边距(下)
                    sheet.setMargin(HSSFSheet.LeftMargin, (double) 0.1);// 页边距(左)
                    sheet.setMargin(HSSFSheet.RightMargin, (double) 0.1);// 页边距(右)
                    sheet.setMargin(HSSFSheet.TopMargin, (double) 0.5);// 页边距(上)
                    sheet.setHorizontallyCenter(true);//设置打印页面为水平居中
                    sheet.setVerticallyCenter(false);//设置打印页面为垂直居中使用POI输出Excel时打印页面的设置 - TOUGHGUYNEU - @EXPLORER使用POI输出Excel时打印页面的设置 - TOUGHGUYNEU - @EXPLORER
                }
            }
            //将文件存到浏览器设置的下载位置
            exportExcelByDownload(workbook, response, "批量导出Excel");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
  /**
     * @Description: 解决合并单元格没有边框(边框被覆盖掉)
     * @MethodName: setRegionBorder
     * @Param: [border, region, sheet]
     * @Return: void
     * @Date: 2020-11-05
    **/
    private void setRegionBorder(BorderStyle border, CellRangeAddress region, Sheet sheet) {
        RegionUtil.setBorderBottom(border, region, sheet);
        RegionUtil.setBorderLeft(border, region, sheet);
        RegionUtil.setBorderRight(border, region, sheet);
        RegionUtil.setBorderTop(border, region, sheet);
    }
    /**
     * excel表格直接下载
     */
    public static void exportExcelByDownload(HSSFWorkbook wb, HttpServletResponse httpServletResponse, String fileName) throws Exception {
        //响应类型为application/octet- stream情况下使用了这个头信息的话,那就意味着不想直接显示内容
        httpServletResponse.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
        //attachment为以附件方式下载
        SimpleDateFormat newsdf = new SimpleDateFormat("yyyyMMddHHmmss");
        String date = newsdf.format(new Date());
        httpServletResponse.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(
                fileName + date + ".xls",
                "utf-8"));
        /**
         * 代码里面使用Content-Disposition来确保浏览器弹出下载对话框的时候。
         * response.addHeader("Content-Disposition","attachment");一定要确保没有做过关于禁止浏览器缓存的操作
         */
        httpServletResponse.setHeader("Cache-Control", "No-cache");
        httpServletResponse.flushBuffer();

        wb.write(httpServletResponse.getOutputStream());
        wb.close();
    }
/**
 * @Description: 删除rowIndex行,并且上移其它行
 * @MethodName: removeRow
 * @Param: [sheet, rowIndex]
 * @Return: void
 * @Date: 2020-11-05
**/
private static void removeRow(HSSFSheet sheet, int rowIndex) {
    int lastRowNum=sheet.getLastRowNum();
    if(rowIndex>=0&&rowIndex<lastRowNum){
        sheet.shiftRows(rowIndex+1,lastRowNum,-1);//将行号为rowIndex+1一直到行号为lastRowNum的单元格全部上移一行,以便删除rowIndex行
    }
    if(rowIndex==lastRowNum){
        HSSFRow removingRow=sheet.getRow(rowIndex);
        if(removingRow!=null)
            sheet.removeRow(removingRow);
    }
}

致谢

JAVA POI的使用_林中静月下仙的博客-CSDN博客
POI3切换为POI4修改内容

总结

提示:写完后,我突然感觉为什么不封装个方法,循环数组即可,内容定义在数组里。虽然我也封装了不少,算了毕竟第一次使用POI,下回再说。

綜上:最好使用封装POI的工具(比如hutool等)去做导入导出,毕竟几行代码就能搞定,面对复杂的文件导出建议使用模板引擎。如果前者都不适用,那就选POI吧,POI功能强大,无所不能,无法无天,哈哈哈。

喜欢就关注我吧,我会努力更新的!