Apache poi 导出数据到Excel

apache poi 介绍

Apache POI 是一个开源的 Java 库,可以用来读写 Microsoft Office 文档,包括 Word、Excel、PowerPoint 等各种格式。

maven依赖

用的是最新的版本 5.3.0

        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi</artifactId>
            <version>5.3.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>5.3.0</version>
        </dependency>

技术介绍

在导出的过程中使用到了SXSSFWorkbook ,是从POI 3.8版本开始,提供的一种基于XSSF的低内存占用的API,能有效的避免导出数量过大时,OOM问题。

引用官方的介绍,简单概括就是:
SXSSF是对XSSF的一种流式扩展,特点和原理是采用了滑动窗口的机制,低内存占用,主要用于数据量非常大的电子表格而虚拟机堆有限的情况。

SXSSFWorkbook.DEFAULT_WINDOW_SIZE默认值是100,表示在内存中最多存在100个Row对象,当写第101个Row对象的时候就会把第1个Row对象以XML格式写入C:\Users\wange\AppData\Local\Temp路径下的临时文件中,后面的以此类推,始终保持内存中最多存在100个Row对象。

SXSSFWorkbook默认使用内联字符串而不是共享字符串表(SharedStringsTable)。启用共享字符串时,文档中的所有唯一字符串都必须保存在内存中,因此XSSF会占用更多的内存。

与XSSF的对比,在一个时间点上,只可以访问一定数量的Row;不再支持Sheet.clone();不再支持公式的求值。但是除了滑动窗口,其余的EXCLE操作仍然使用的是XSSF的API。

导出流程

1.创建Workbook对象
2.创建sheet工作表
3.穿件Row行
4.创建Cell单元格
5.写入单元格数据,一个格式
6.导出字节流

代码

直接看代码
FileExportController.java

@RestController
public class FileController {
	
	@Autowired
    private FileExport fileExport;
    /** 
     * excel导出接口
     * 这里自定义了一些导出数据
     **/
    @GetMapping("export")
    public ResponseEntity<InputStreamResource> export(){

        List<Object[]> dataList = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            Object[] objects = new Object[10];
            objects[0] = "1000"+i;
            objects[1] = "学生"+(i+1);
            objects[2] = "实验班";
            objects[3] = i*5;
            dataList.add(objects);
        }
        ExportExcelData of = ExportExcelData.of("导出测试",
                new String[]{"学号","姓名","班级","成绩"},dataList);
        ByteArrayOutputStream outputStream = (ByteArrayOutputStream) fileExport.excelExport(of,new ExportDataConsumer());

        byte[] bytes = outputStream.toByteArray();

        InputStreamResource resource = new InputStreamResource(new ByteArrayInputStream(bytes));
        return ResponseEntity.ok()
                .header("Content-Disposition", "attachment; filename=test-export.xlsx")
                .body(resource);

    }
}

导出数据实体类,导出的数据封装成一个javabean实体

public class ExportExcelData {

    //显示的导出表的标题
    private final String title;
    //导出表的列名
    private final String[] colName;
    //列的数量
    private final Integer colNum;
    //列中的数据
    private final List<Object[]> dataList;


    public ExportExcelData(String title,String[] colName,List<Object[]> dataList){
        this.title = title;
        this.colName = colName;
        this.dataList = dataList;
        colNum = colName.length;
    }

    public static ExportExcelData of(String title,String[] colName,List<Object[]> dataList){
        return new ExportExcelData(title,colName,dataList);
    }
    public static ExportExcelData of(String title,String[] colName){
        return new ExportExcelData(title,colName,null);
    }

    public String getTitle() {
        return title;
    }

    public String[] getColName() {
        return colName;
    }

    public Integer getColNum() {
        return colNum;
    }

    public List<Object[]> getDataList() {
        return dataList;
    }
}

导出数据apache poi 核心逻辑类
在方法个编写的时候增加了一个Consmer 参数,可以在调用方法时,自定义导出数据的格式。如单元格合并导出,单元格显示图片等等。

public interface FileExport {

    default OutputStream excelExport(ExportExcelData exportExcelData, Consumer<Map<String,Object>> function){
        throw new RuntimeException("未实现方法");
    };
}

接口实现

public class FileExportService implements FileExport {

    private final short titleHeight = 20 * 30;

    /**
     * 让列宽随着导出的列长自动适应
     * @param sheet
     */
    private void columnWidthAdaptive(Sheet sheet, int columnNum, CellStyle cellStyle) {
        for (int colNum = 0; colNum < columnNum; colNum++) {
            int columnWidth = sheet.getColumnWidth(colNum) / 256;
            for (int rowNum = 0; rowNum <= sheet.getLastRowNum(); rowNum++) {
                Row currentRow;
                //当前行未被使用过
                if (sheet.getRow(rowNum) == null) {
                    currentRow = sheet.createRow(rowNum);
                } else {
                    currentRow = sheet.getRow(rowNum);
                }
                Cell currentCell;
                if (currentRow.getCell(colNum) != null) {
                    currentCell = currentRow.getCell(colNum);
                } else {
                    currentCell = currentRow.createCell(colNum);
                }
                currentCell.setCellStyle(cellStyle);
                int length = currentCell.getStringCellValue().getBytes().length;
                if (columnWidth < length) {
                    columnWidth = length;
                }
            }
            sheet.setColumnWidth(colNum, (columnWidth + 4) * 256);
        }
    }

    public void setRegionStyle(Sheet sheet, CellRangeAddress range, CellStyle cs) {
        for (int i = range.getFirstRow(); i <= range.getLastRow(); i++) {
            Row row = sheet.getRow(i);
            if (row == null){
                row = sheet.createRow(i);
            }
            for (int j = range.getFirstColumn(); j <= range.getLastColumn(); j++) {
                Cell cell = row.getCell(j);
                if (cell == null){
                    cell = row.createCell(j);
                }
                cell.setCellStyle(cs);
            }
        }
    }


    @Override
    public OutputStream excelExport(ExportExcelData exportExcelData, Consumer<Map<String,Object>> consumer) {

        String[] colName = exportExcelData.getColName();
        Integer colNum = exportExcelData.getColNum();

        OutputStream outputStream = new ByteArrayOutputStream();
        //创建工作表
        SXSSFWorkbook workbook = new SXSSFWorkbook();
        SXSSFSheet sheet = workbook.createSheet(exportExcelData.getTitle());


        //标题单元格样式
        Font titleFont = ExcelCellStyle.fontStyle(workbook, (short) 12,true);
        CellStyle titleCellStyle = ExcelCellStyle.columnStyle(workbook, titleFont);
        //写入标题单元格数据
        SXSSFRow rowTitle = sheet.createRow(0);
        SXSSFCell cellTitle = rowTitle.createCell(0, CellType.STRING);
        cellTitle.setCellValue(exportExcelData.getTitle());

        rowTitle.setHeight(titleHeight);
        //合并标题单元格
        CellRangeAddress cellAddresses = new CellRangeAddress(0, 1, 0, (colNum - 1));
        sheet.addMergedRegion(cellAddresses);
        setRegionStyle(sheet,cellAddresses,titleCellStyle);

        //定义列头数据
        SXSSFRow rowTow = sheet.createRow(2);// 在索引2的位置创建行(最顶端的行开始的第二行)
        rowTow.setHeight(titleHeight); //设置高度
        // 设置单元格样式
        Font rowTowFont = ExcelCellStyle.fontStyle(workbook, (short) 11,false);
        CellStyle rowTowCellStyle = ExcelCellStyle.columnStyle(workbook, rowTowFont);
        // 将列头设置到sheet的单元格中
        for (int n = 0; n < colNum; n++) {
            SXSSFCell cell = rowTow.createCell(n);
            cell.setCellType(CellType.STRING);                //设置列头单元格的数据类型
            HSSFRichTextString text = new HSSFRichTextString(colName[n]);
            cell.setCellValue(text);                                    //设置列头单元格的值
            cell.setCellStyle(rowTowCellStyle);                        //设置列头单元格样式
        }


        //数据写入表格
        List<Object[]> dataList = exportExcelData.getDataList();
        Map<String, Object> map = new HashMap<String, Object>() {{
            put("obj", dataList);put("workbook",workbook);put("sheet",sheet);
        }};
        consumer.accept(map);

        //让列宽随着导出的列长自动适应 并这是样式
        CellStyle cellStyle = ExcelCellStyle.defaultCellStyle(workbook);
        columnWidthAdaptive(sheet,colNum,cellStyle);

        try {
            workbook.write(outputStream);
            workbook.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return outputStream;
    }

}

导出书记写入默认Consumer实现

public class ExportDataConsumer implements Consumer<Map<String,Object>> {

    @Override
    public void accept(Map<String, Object> stringObjectMap) {

        Sheet sheet = (Sheet) stringObjectMap.get("sheet");
        List<Object[]> dataList = (List<Object[]>) stringObjectMap.get("obj");
        for (int i = 0; i < dataList.size(); i++) {
            Object[] objects = dataList.get(i);
            Row row = sheet.createRow(i+3);//创建所需的行数
            row.setHeight((short) (25 * 20)); //设置高度
            for (int j = 0; j < objects.length; j++) {
                Cell cell = row.createCell(j, CellType.STRING);
                if (objects[j] != null) {
                    cell.setCellValue(objects[j].toString()); //设置单元格的值
                }
            }
        }

    }
}

导出文件效果

导出效果

总结

综上就是一个简单的数据导出excel接口逻辑,当前的逻辑比较简单,如需要复杂的导出数据以及格式(如单元格合并,单元格插入图片等等),还需自己实现。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值