poi导出excel抽象模版

poi导出excel抽象模版

文中使用到的相关jar包和版本,org.apache.poi:poi-ooxml:3.17,下面是文中主要涉及到的相关知识点:

  • 利用模版模式构建导出操作
  • 兼容IE浏览器下中文文件名乱码
  • 使用SXSSFWorkbook操作sheet,避免oom
  • 可设置是否需要excel表头、表尾
  • 导出的excel单元格对日期类型,数字类型做转换(主要是去掉单元格左上角的小绿帽)

代码块

核心代码如下:

import com.dy.analysis.utils.UrlUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.hssf.util.HSSFColor;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.streaming.SXSSFCell;
import org.apache.poi.xssf.streaming.SXSSFRow;
import org.apache.poi.xssf.streaming.SXSSFSheet;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.usermodel.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.OutputStream;
import java.util.List;
import java.util.Map;

public abstract class AbstractExportTemplate<T> {


    private String sheetName = "sheet";
    private String[] titleName;
    private List<T> dataList;
    private List<Map<String, Object>> footList;
    private boolean needHeader = false;
    private int sheetMaxRowCount = 10000;
    private String headerName = sheetName;
    private static final String POINT = ".";
    private static final String XLS = POINT + "xls";
    private static final String XLSX = POINT + "xlsx";
    public static final String IE = "msie";
    public static final String IE_11 = "trident";
    public static final String WIN_10 = "edge";

    public void download(HttpServletRequest request, HttpServletResponse response, String fileName) throws Exception {
        fileName = initFileSuffix(fileName);
        String userAgent = request.getHeader("User-Agent").toLowerCase();
        String formFileName = fileName;
        //对ie内核的浏览器文件名乱码处理
        if (userAgent.contains(IE) || userAgent.contains(IE_11) || userAgent.contains(WIN_10)) {
            formFileName = UrlUtils.encode(formFileName);
            formFileName = formFileName.replace("+", "%20");
        } else {
            formFileName = new String(fileName.getBytes(UrlUtils.UTF_8), UrlUtils.ISO);
        }
        String headStr = "attachment; filename=\"" + formFileName + "\"";
        response.setContentType("application/octet-stream");
        response.setHeader("Content-Disposition", headStr);
        OutputStream out = response.getOutputStream();
        initExport().write(out);
        out.close();
    }

    /**
     * 拼装excel表内容,由子类实现
     *
     * @param sheetRowNum      每个sheet的初始行号
     * @param sheetMaxRowCount 每个sheet的最大数据条数
     * @param sheet            sheet对象
     * @param style            每个单元格样式对象
     * @param workbook         workbook对象
     */
    protected abstract void packageData(int sheetRowNum, int sheetMaxRowCount, SXSSFSheet sheet, CellStyle style, SXSSFWorkbook workbook);

    /**
     * 拼装excel表底部内容,由子类实现,非必须
     *
     * @param sheetMaxRowCount 每个sheet的最大数据条数
     * @param sheet            sheet对象
     * @param style            每个单元格样式对象
     * @param workbook         workbook对象
     */
    protected abstract void packageFoot(int sheetMaxRowCount, SXSSFSheet sheet, CellStyle style, SXSSFWorkbook workbook);


    /**
     * 组装数据前的准备工作
     */
    private Workbook initExport() {
        SXSSFWorkbook workbook = new SXSSFWorkbook(100);
        CellStyle columnTopStyle = getColumnTopStyle(workbook);
        CellStyle style = getStyle(workbook);
        int rowsCount = dataList.size();
        int sheetIndex = rowsCount / sheetMaxRowCount;
        for (int a = 0; a <= sheetIndex; a++) {
            SXSSFSheet sheet = workbook.createSheet(sheetName + a);
            if (needHeader) {
                SXSSFRow rowm = sheet.createRow(0);
                SXSSFCell cellTiltle = rowm.createCell(0);
                sheet.addMergedRegion(new CellRangeAddress(0, 1, 0, (titleName.length - 1)));
                cellTiltle.setCellStyle(columnTopStyle);
                cellTiltle.setCellValue(headerName);
            }

            int columnNum = titleName.length;
            SXSSFRow rowName;
            if (needHeader) {
                // 在索引2的位置创建行(最顶端的行开始的第三行)
                rowName = sheet.createRow(2);
            } else {
                // 在索引0的位置创建行(最顶端的行开始的第一行)
                rowName = sheet.createRow(0);
            }
            for (int n = 0; n < columnNum; n++) {
                SXSSFCell cellRowName = rowName.createCell(n);
                cellRowName.setCellType(CellType.STRING);
                XSSFRichTextString text = new XSSFRichTextString(titleName[n]);
                cellRowName.setCellValue(text);
                cellRowName.setCellStyle(columnTopStyle);
                sheet.setColumnWidth(n, (text.toString().getBytes().length + 8) * 256);
            }
            int sheetRowNum = a * sheetMaxRowCount;
            int lastRowCount = rowsCount % sheetMaxRowCount;
            if (a == sheetIndex) {
                sheetMaxRowCount = lastRowCount;
            }
            //组装具体数据
            packageData(sheetRowNum, sheetMaxRowCount, sheet, style, workbook);
            packageFoot(sheetMaxRowCount, sheet, getFootStyle(workbook), workbook);
        }
        return workbook;
    }


    /**
     * 表头单元格样式
     *
     * @param workbook workbook对象
     * @return 单元格样式对象
     */
    private CellStyle getColumnTopStyle(SXSSFWorkbook workbook) {
        Font font = workbook.createFont();
        font.setFontHeightInPoints((short) 11);
        font.setBold(true);
        font.setFontName("微软雅黑");
        CellStyle style = workbook.createCellStyle();
        style.setBorderBottom(BorderStyle.THIN);
        style.setBottomBorderColor(HSSFColor.HSSFColorPredefined.BLACK.getIndex());
        style.setBorderLeft(BorderStyle.THIN);
        style.setLeftBorderColor(HSSFColor.HSSFColorPredefined.BLACK.getIndex());
        style.setBorderRight(BorderStyle.THIN);
        style.setRightBorderColor(HSSFColor.HSSFColorPredefined.BLACK.getIndex());
        style.setBorderTop(BorderStyle.THIN);
        style.setTopBorderColor(HSSFColor.HSSFColorPredefined.BLACK.getIndex());
        style.setFont(font);
        style.setWrapText(false);
        style.setAlignment(HorizontalAlignment.CENTER);
        style.setVerticalAlignment(VerticalAlignment.CENTER);
        return style;
    }

    /**
     * 表格单元格样式
     *
     * @param workbook workbook对象
     * @return 单元格样式对象
     */
    private CellStyle getStyle(SXSSFWorkbook workbook) {
        Font font = workbook.createFont();
        font.setFontHeightInPoints((short) 10);
        font.setFontName("微软雅黑");
        CellStyle style = workbook.createCellStyle();
        style.setBorderBottom(BorderStyle.THIN);
        style.setBottomBorderColor(HSSFColor.HSSFColorPredefined.BLACK.getIndex());
        style.setBorderLeft(BorderStyle.THIN);
        style.setLeftBorderColor(HSSFColor.HSSFColorPredefined.BLACK.getIndex());
        style.setBorderRight(BorderStyle.THIN);
        style.setRightBorderColor(HSSFColor.HSSFColorPredefined.BLACK.getIndex());
        style.setBorderTop(BorderStyle.THIN);
        style.setTopBorderColor(HSSFColor.HSSFColorPredefined.BLACK.getIndex());
        style.setFont(font);
        style.setWrapText(true);
        style.setAlignment(HorizontalAlignment.CENTER);
        style.setVerticalAlignment(VerticalAlignment.CENTER);
        return style;
    }

    /**
     * 表尾单元格样式,一般为总计,合计等
     *
     * @param workbook workbook对象
     * @return 单元格样式对象
     */
    private CellStyle getFootStyle(SXSSFWorkbook workbook) {
        Font font = workbook.createFont();
        font.setFontHeightInPoints((short) 10);
        font.setBold(true);
        font.setFontName("微软雅黑");
        CellStyle style = workbook.createCellStyle();
        style.setBorderBottom(BorderStyle.THIN);
        style.setBottomBorderColor(HSSFColor.HSSFColorPredefined.BLACK.getIndex());
        style.setBorderLeft(BorderStyle.THIN);
        style.setLeftBorderColor(HSSFColor.HSSFColorPredefined.BLACK.getIndex());
        style.setBorderRight(BorderStyle.THIN);
        style.setRightBorderColor(HSSFColor.HSSFColorPredefined.BLACK.getIndex());
        style.setBorderTop(BorderStyle.THIN);
        style.setTopBorderColor(HSSFColor.HSSFColorPredefined.BLACK.getIndex());
        style.setFont(font);
        style.setWrapText(true);
        style.setAlignment(HorizontalAlignment.CENTER);
        style.setVerticalAlignment(VerticalAlignment.CENTER);
        return style;
    }

    private String initFileSuffix(String fileName) {
        if (StringUtils.isEmpty(fileName)) {
            fileName = System.currentTimeMillis() + XLSX;
        } else {
            if (fileName.lastIndexOf(POINT) != -1) {
                if (!fileName.endsWith(XLS) && !fileName.endsWith(XLSX)) {
                    throw new IllegalArgumentException("The fileName's type is not support,[" + fileName + "]");
                }
            } else {
                fileName = fileName + XLSX;
            }
            if (fileName.endsWith(XLS)) {
                fileName = fileName.substring(0, fileName.lastIndexOf(".")) + XLSX;
            }
        }
        return fileName;
    }

    public void setSheetMaxRowCount(int sheetMaxRowCount) {
        this.sheetMaxRowCount = sheetMaxRowCount;
    }

    public void setNeedHeader(boolean needHeader) {
        this.needHeader = needHeader;
    }

    public void setHeaderName(String headerName) {
        this.headerName = headerName;
    }

    void setTitleName(String[] titleName) {
        this.titleName = titleName;
    }

    void setDataList(List<T> dataList) {
        this.dataList = dataList;
    }

    void setFootList(List<Map<String, Object>> footList) {
        this.footList = footList;
    }

    void setSheetName(String sheetName) {
        this.sheetName = sheetName;
    }

    String[] getTitleName() {
        return titleName;
    }

    List<T> getDataList() {
        return dataList;
    }

    List<Map<String, Object>> getFootList() {
        return footList;
    }

    boolean isNeedHeader() {
        return needHeader;
    }

}

利用的模版模式,把导出相关的公共操作定义到抽象类里。子类只需实现具体拼装表格的方法,以及定义导出数据对象的泛型即可。文件名乱码这个经测试ie11,edge,chrome,firefox均不会出现乱码的问题。
例如下面的demo,其中如果不需要表尾的话,packageFoot方法实现后为空即可:

import com.dy.analysis.dto.TestDTO;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.xssf.streaming.SXSSFCell;
import org.apache.poi.xssf.streaming.SXSSFRow;
import org.apache.poi.xssf.streaming.SXSSFSheet;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;

import java.util.List;
import java.util.Map;


public class TestExcel extends AbstractExportTemplate<TestDTO> {


    public TestExcel(String sheetName, String[] titleName, List<TestDTO> dataList, List<Map<String, Object>> footList) {
        setSheetName(sheetName);
        setTitleName(titleName);
        setDataList(dataList);
        setFootList(footList);
    }

    @Override
    protected void packageData(int sheetRowNum, int sheetMaxRowCount, SXSSFSheet sheet, CellStyle style, SXSSFWorkbook workbook) {
        CellStyle dateStyle = workbook.createCellStyle();
        dateStyle.cloneStyleFrom(style);
        dateStyle.setDataFormat(workbook.createDataFormat().getFormat("yyyy-mm-dd hh:mm:ss"));
        for (int i = 0; i < sheetMaxRowCount; i++) {
            SXSSFRow row;
            if (isNeedHeader()) {
                //需加上表头占用的两行
                row = sheet.createRow(i + 3);
            } else {
                row = sheet.createRow(i + 1);
            }
            TestDTO dto = getDataList().get(sheetRowNum + i);
            SXSSFCell cell = row.createCell(0, CellType.STRING);
            cell.setCellValue(dto.getDate());
            cell.setCellStyle(dateStyle);
            SXSSFCell cell2 = row.createCell(1, CellType.STRING);
            cell2.setCellValue(dto.getUsername());
            cell2.setCellStyle(style);
            SXSSFCell cell3 = row.createCell(2, CellType.STRING);
            cell3.setCellValue(dto.getNickname());
            cell3.setCellStyle(style);
            SXSSFCell cell4 = row.createCell(3, CellType.NUMERIC);
            cell4.setCellValue(dto.getAge());
            cell4.setCellStyle(style);
        }
    }

    @Override
    protected void packageFoot(int sheetMaxRowCount, SXSSFSheet sheet, CellStyle style, SXSSFWorkbook workbook) {
        if (getFootList() == null || getFootList().isEmpty()) {
            return;
        }
        String allAge = String.valueOf(getFootList().get(0).get("allAge"));
        SXSSFRow row;
        if (isNeedHeader()) {
            //需加上表头占用的两行
            row = sheet.createRow(sheetMaxRowCount + 3);
        } else {
            row = sheet.createRow(sheetMaxRowCount + 1);
        }
        SXSSFCell cell = row.createCell(0, CellType.STRING);
        cell.setCellValue("总计");
        cell.setCellStyle(style);
        SXSSFCell cell2 = row.createCell(1, CellType.STRING);
        cell2.setCellValue("/");
        cell2.setCellStyle(style);
        SXSSFCell cell3 = row.createCell(2, CellType.STRING);
        cell3.setCellValue("/");
        cell3.setCellStyle(style);
        SXSSFCell cell4 = row.createCell(3, CellType.NUMERIC);
        cell4.setCellValue(Double.parseDouble(allAge));
        cell4.setCellStyle(style);
    }
}

下面是入口和测试相关的代码了


import com.dy.analysis.dto.TestDTO;
import com.dy.analysis.export.TestExcel;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


@Controller
@RequestMapping("/export")
public class ExportController {

    @GetMapping("/excel")
    public void excel(HttpServletRequest request, HttpServletResponse response) {
        List<TestDTO> testDTOS = new ArrayList<>();
        TestDTO testDTO = new TestDTO("abc", "abc", 21, "2018-07-01 01:25:45");
        TestDTO testDTO2 = new TestDTO("def", "def", 20, "2018-07-02 01:25:45");
        TestDTO testDTO3 = new TestDTO("hij", "hij", 18, "2018-07-03 01:25:45");
        TestDTO testDTO4 = new TestDTO("klm", "klm", 19, "2018-07-04 01:25:45");
        TestDTO testDTO5 = new TestDTO("nop", "nop", 25, "2018-07-05 01:25:45");
        testDTOS.add(testDTO);
        testDTOS.add(testDTO2);
        testDTOS.add(testDTO3);
        testDTOS.add(testDTO4);
        testDTOS.add(testDTO5);
        List<Map<String, Object>> footList = new ArrayList<>();
        Map<String, Object> map = new HashMap<>();
        map.put("allAge", 125);
        footList.add(map);
        String[] title = new String[]{"日期", "名称", "昵称", "年龄"};
        TestExcel excel = new TestExcel("sheetName", title, testDTOS, footList);
        try {
            //excel.setNeedHeader(true);
            //excel.setHeaderName("Excel Head");
            excel.download(request, response, "啦啦啦,卖报的小玩家");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
import lombok.Getter;
import lombok.Setter;

@Setter
@Getter
public class TestDTO {

    private String username;
    private String nickname;
    private int age;
    private String date;

    public TestDTO(String username, String nickname, int age, String date) {
        this.username = username;
        this.nickname = nickname;
        this.age = age;
        this.date = date;
    }
}

如果需要表头的话把注释的两行放开即可,使用起来也是很简单,new一个导出类的对象,调用download方法即可

下面放两张效果图
没有表头的效果

有表头的效果


其中代码没有什么难度,主要就是一个抽象概念,之前在项目中导出excel用的比较多,代码又比较凌乱,就花了点时间把这块整理了一下

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值