Java POI写入excel数据简单实现

使用POI根据传入的标题、数据、数据格式等生产简单的excel文件,有例子和注解,有兴趣的朋友可以看下:

import com.google.common.collect.Lists;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.hssf.usermodel.*;
import org.apache.poi.ss.usermodel.HorizontalAlignment;

import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 简单excel生成工具(使用POI),配合@PoiExcelFiled注解使用
 * <p>
 * 返回HSSFSheet可以在此基础上新增sheet、获取sheet字节数组
 * </p>
 *
 * @author hzhqk
 * @date 2018/03/21
 */
@Slf4j
public class ExcelUtil {
    /**
     * 默认sheet名称
     */
    private static final String DEFAULT_SHEET_NAME = "sheet";
    /**
     * 单个Sheet页最大行数(除去标题)
     */
    private static final int SINGLE_SHEET_MAX_ROWS = 65535;
    /**
     * 默认单元格宽度
     */
    private static final int DEFAULT_CELL_WIDTH = 5000;
    /**
     * 默认日期格式
     */
    private static final String DEFAULT_DATE_PATTERN = "yyyy-MM-dd HH:mm:ss";

    /**
     * 根据列标题和列数据生成excel表格文件
     * excel默认一个sheet且sheet名称默认为sheet1
     * <pre>
     *     获取excel bytes示例:
     *         ByteArrayOutputStream os = new ByteArrayOutputStream();
     *         workbook.write(os);
     *         或直接使用 PoiExcelUtil.getExcelBytes(hssfWorkbook);
     * </pre>
     *
     * @param datas
     * @return 没有使用write方法时要手动关闭HSSFWorkbook
     * @throws Exception
     */
    public static HSSFWorkbook createExcel(List<?> datas) throws Exception {
        return createExcelWithSheetName(null, datas);
    }

    /**
     * 根据列标题和列数据生成excel表格文件,excel文件默认一个sheet
     *
     * @param sheetName 生成的excel文件的sheet名,分页时会在后面加序号
     * @param datas     列数据
     * @return 文件byte
     * @throws Exception
     */
    public static HSSFWorkbook createExcelWithSheetName(String sheetName, List<?> datas) throws Exception {
        HSSFWorkbook workbook = new HSSFWorkbook();
        createSheetAndWriteData(workbook, sheetName, datas);
        return workbook;
    }

    /**
     * 在现有HSSFWorkbook基础上新建sheet页
     *
     * @param workbook
     * @param sheetName
     * @param datas
     * @return
     * @throws Exception
     */
    public static HSSFWorkbook createSheetAndWriteData(HSSFWorkbook workbook, String sheetName, List<?> datas) throws Exception {
        if (StringUtils.isBlank(sheetName)) {
            sheetName = getDefaultSheetName(workbook, workbook.getNumberOfSheets());
        }
        if (CollectionUtils.isEmpty(datas)) {
            log.info("数据为空,不写入数据");
            // 创建一个空sheet,防止打开报错
            workbook.createSheet(sheetName);
            return workbook;
        }
        List<Field> allFields = getAllFields(datas.get(0).getClass());
        List<Field> dataFields = allFields.stream().filter(f -> f.getAnnotation(ExcelField.class) != null).collect(Collectors.toList());
        if (datas.size() > SINGLE_SHEET_MAX_ROWS) {
            List<? extends List<?>> parts = Lists.partition(datas, SINGLE_SHEET_MAX_ROWS);
            int sheetIndex = 1;
            for (List<?> part : parts) {
                createOneSheetAndWriteData(workbook, sheetName + sheetIndex++, part, dataFields);
            }
        } else {
            createOneSheetAndWriteData(workbook, sheetName, datas, dataFields);
        }
        return workbook;
    }

    private static String getDefaultSheetName(HSSFWorkbook workbook, int sheetNum) {
        if (sheetNum == 0) {
            return DEFAULT_DATE_PATTERN + 1;
        }
        String sheetName = DEFAULT_SHEET_NAME + (sheetNum + 1);
        if (workbook.getSheetIndex(sheetName) >= 0) {
            sheetName = getDefaultSheetName(workbook, sheetNum + 1);
        }
        return sheetName;
    }

    private static void createOneSheetAndWriteData(HSSFWorkbook workbook, String sheetName, List<?> datas, List<Field> dataFields) throws Exception {
        HSSFSheet sheet = workbook.createSheet(sheetName);
        List<FieldWithFormatter> fieldWithFormatter = convert2FieldWithFormatter(dataFields);
        initSheetHeaders(workbook, sheet, fieldWithFormatter);
        writeData(sheet, datas, fieldWithFormatter);
    }

    /**
     * 将HSSFWorkbook转为bytes
     *
     * @param workbook
     * @return
     * @throws IOException
     */
    public static byte[] getExcelBytes(HSSFWorkbook workbook) throws IOException {
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        workbook.write(os);
        return os.toByteArray();
    }

    /**
     * 传到前台
     *
     * @param workbook
     * @param response
     */
    public static void write2Response(HSSFWorkbook workbook, String fileName, HttpServletResponse response) throws IOException {
        response.setHeader("Content-Disposition", "attachment;filename=" + new String((fileName).getBytes(), "ISO-8859-1"));
        response.setContentType("application/x-execl");
        workbook.write(response.getOutputStream());
    }

    private static void writeData(HSSFSheet sheet, List<?> datas, List<FieldWithFormatter> fieldWithFormatter) throws Exception {
        if (CollectionUtils.isEmpty(datas)) {
            return;
        }
        int startX = 1;
        int startY = 0;
        for (Object item : datas) {
            HSSFRow row = sheet.createRow(startX);
            for (FieldWithFormatter fwf : fieldWithFormatter) {
                HSSFCell cell = row.createCell(startY++);
                Object origin = fwf.getField().get(item);
                String columnData = "";
                if (null != origin) {
                    ExcelColumnFormatter formatter = fwf.getFormatter();
                    if (formatter != null) {
                        columnData = formatter.format(origin);
                    } else {
                        columnData = origin.toString();
                    }
                }
                cell.setCellValue(columnData);
            }
            startX++;
            startY = 0;
        }
    }

    private static List<FieldWithFormatter> convert2FieldWithFormatter(List<Field> fields) {
        return fields.stream().map(f -> {
            ExcelField annotation = f.getAnnotation(ExcelField.class);
            ExcelColumnFormatter formatter = null;
            try {
                Class<? extends ExcelColumnFormatter> format = annotation.formatter();
                if (format != NoFormatter.class) {
                    formatter = format.newInstance();
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
            return new FieldWithFormatter(f, annotation.name(), annotation.order(), formatter);
        }).sorted(Comparator.comparingInt(f -> f.order)).collect(Collectors.toList());
    }

    private static class FieldWithFormatter {
        private Field field;
        private String columnName;
        private int order;
        private ExcelColumnFormatter formatter;

        public FieldWithFormatter(Field field, String columnName, int order, ExcelColumnFormatter formatter) {
            this.field = field;
            this.columnName = columnName;
            this.order = order;
            this.formatter = formatter;
        }

        public Field getField() {
            return field;
        }

        public void setField(Field field) {
            this.field = field;
        }

        public String getColumnName() {
            return columnName;
        }

        public void setColumnName(String columnName) {
            this.columnName = columnName;
        }

        public int getOrder() {
            return order;
        }

        public void setOrder(int order) {
            this.order = order;
        }

        public ExcelColumnFormatter getFormatter() {
            return formatter;
        }

        public void setFormatter(ExcelColumnFormatter formatter) {
            this.formatter = formatter;
        }
    }

    private static <T> List<Field> getAllFields(Class<T> clz) {
        List<Field> fields = Lists.newArrayList();
        Class<?> tmpClz = clz;
        // 不获取Object层的属性
        String finalParent = "java.lang.object";
        while (tmpClz != null && !tmpClz.getName().toLowerCase().equals(finalParent)) {
            // 只获取bean普通属性
            for (Field field : tmpClz.getDeclaredFields()) {
                // 不在设置数据时设置访问权限
                field.setAccessible(true);
                int modifiers = field.getModifiers();
                if (modifiers == Modifier.PUBLIC || modifiers == Modifier.PRIVATE || modifiers == Modifier.PROTECTED) {
                    fields.add(field);
                }
            }
            tmpClz = tmpClz.getSuperclass();
        }
        return fields;
    }

    /**
     * 初始化表头
     *  @param wb
     * @param sheet
     * @param dataFields
     */
    private static void initSheetHeaders(HSSFWorkbook wb, HSSFSheet sheet, List<FieldWithFormatter> dataFields) {
        if (CollectionUtils.isEmpty(dataFields)) {
            return;
        }
        // 表头样式
        HSSFCellStyle style = wb.createCellStyle();
        // 创建一个居中格式
        style.setAlignment(HorizontalAlignment.CENTER);
        // 字体样式
        HSSFFont fontStyle = wb.createFont();
        fontStyle.setFontName("微软雅黑");
        fontStyle.setFontHeightInPoints((short) 12);
        fontStyle.setBold(true);
        style.setFont(fontStyle);
        // 生成sheet1内容
        // 第一个sheet的第一行为标题
        HSSFRow rowFirst = sheet.createRow(0);
        // 冻结第一行
        sheet.createFreezePane(0, 1, 0, 1);
        // 写标题
        for (int i = 0; i < dataFields.size(); i++) {
            // 获取第一行的每个单元格
            HSSFCell cell = rowFirst.createCell(i);
            // 设置每列的列宽
            sheet.setColumnWidth(i, DEFAULT_CELL_WIDTH);
            //加样式
            cell.setCellStyle(style);
            //往单元格里写数据
            cell.setCellValue(dataFields.get(i).getColumnName());
        }
    }

    /**
     * test case 运行前临时注释掉servlet-api包的scope,测试完放开
     *
     * @param args
     * @throws Exception
     */
    public static void main(String[] args) throws Exception {
        String str = "字符串";
        Date date = new Date();
        int mint = 12345;
        float mfloat = 1.2345f;

        List<Date> list = Arrays.asList(date, date, date);
        Map<String, Date> map = new HashMap<String, Date>();
        map.put("date1", date);
        map.put("data2", date);

        TestData mb = new TestData(str, date, mint, mfloat, new BigDecimal("0.9999"), list, map);
        List<TestData> datas = Lists.newArrayList();
        for (int i = 0; i < 70000; i++) {
            datas.add(mb);
        }
        HSSFWorkbook wb = ExcelUtil.createExcelWithSheetName("测试", datas);
        ExcelUtil.createSheetAndWriteData(wb, "", Arrays.asList(new Extension(9999)));
        wb.write(new FileOutputStream(new File("/Users/shhanqiankun/Desktop/excel.xls")));
        System.out.println("everything goes well");
    }

    @AllArgsConstructor
    @Data
    private static class Extension {
        @ExcelField(name = "column")
        private Integer total;
    }

    @AllArgsConstructor
    @Data
    private static class TestData {
        @ExcelField(name = "String", order = 9)
        private String str;
        @ExcelField(name = "Date", order = 8, formatter = DefaultDateFormatter.class)
        private Date date;
        @ExcelField(name = "Integer", order = 7)
        private int mint;
        @ExcelField(name = "Float", order = 6)
        private float mfloat;
        @ExcelField(name = "Decimal", order = 3)
        private BigDecimal bigDecimal;
        @ExcelField(name = "list", order = 999)
        private List<Date> list;
        @ExcelField(name = "Map")
        private Map<String, Date> map;
    }
}

效果如图:


simple excel.png

详细源码见:
https://github.com/hzhqk/java/tree/master/poi/simple%20excel

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,491评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,856评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,745评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,196评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,073评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,112评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,531评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,215评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,485评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,578评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,356评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,215评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,583评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,898评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,497评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,697评论 2 335