博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
初识Apache POI框架(讲述如何操作Excel文档)
阅读量:6869 次
发布时间:2019-06-26

本文共 18275 字,大约阅读时间需要 60 分钟。

hot3.png

Apache POI项目官网:

参考文档:

采用用户模式解析Excel文件:

解析大数据量XLSX文件(Apache官方的一个Demo):

解析大数据量XLS文件(也是Apache官方的一个Demo):

写入大数据量(仅支持XLSX后缀,Apache官方的一个Demo):

官方更多Demo:

==============================================================

前言&注意

此项目已被打成Jar包上传到了Maven中央仓库,若你使用Maven管理项目,那么你可以直接引用到你的项目中使用:

com.github.flyinghe
common-utils
2.3.3-RELEASE

由于代码量偏多,有的依赖其他类的代码并未贴出,为节省篇幅只贴出了核心代码。详细代码你可以参考托管在Github上的代码。并且你也可以使用Maven依赖直接使用这些库,使用时同大多数开源项目一样你需要遵循相关开源协议,具体使用哪种开源协议项目中已阐述

==============================================================

1.引入依赖:

org.apache.poi
poi-ooxml
3.14

 

2.用于读取小数据量的方式

由于此方法在读取Excel文件时会加载整个Excel文档到内存,所以此方式适用于读取小数据量的文件。

代码如下:

package at.flying.tools;import org.apache.commons.lang.time.DateUtils;import org.apache.poi.ss.usermodel.*;import java.io.File;import java.math.BigDecimal;import java.math.RoundingMode;import java.util.ArrayList;import java.util.HashMap;import java.util.List;import java.util.Map;/** * 本类用于读取Excel文档,使用POI(3.14及以上版本)技术解析,使用时需导入相关Jar包。* 注意:读取的Excel文件格式为,第0行(0-based)为标题行(可作为列名行列名的解释说明), * 第1行为列名行(存储在Map中时作为key),后面的行称为数据行(存储在Map时的value), * Excel文件中每一个Sheet的格式均是这样 * * @author Flying */public class ReadExcelUtils {    private static String[] pattern =            new String[]{"yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm:ss.S", "yyyy.MM.dd",                    "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm:ss.S", "yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss",                    "yyyy/MM/dd HH:mm:ss.S",};    private ReadExcelUtils() {}    /**     * 获取指定单元格的内容。只能是日期(返回java.util.Date),整数值(返回的均是String类型),小数值(返回的均是String类型,并     * 保留两位小数),字符串, 布尔类型。 其他类型返回null。     *     * @param type 指定类型(Cell中定义的类型)     * @param cell 指定单元格     * @return 返回Cell里的内容     */    public static Object getCellValue(int type, Cell cell) {        switch (type) {            case Cell.CELL_TYPE_BOOLEAN:                return cell.getBooleanCellValue();            case Cell.CELL_TYPE_NUMERIC:                if (DateUtil.isCellDateFormatted(cell)) {                    return DateUtil.getJavaDate(cell.getNumericCellValue());                } else {                    BigDecimal bd = new BigDecimal(cell.getNumericCellValue());                    return bd.toString().contains(".") ? bd.setScale(2, RoundingMode.HALF_UP).toString() :                            bd.toString();                }            case Cell.CELL_TYPE_STRING:                try {                    return DateUtils.parseDate(cell.getStringCellValue(), ReadExcelUtils.pattern);                } catch (Exception e) {                    return cell.getStringCellValue();                }            case Cell.CELL_TYPE_FORMULA:                if (DateUtil.isCellDateFormatted(cell)) {                    return cell.getDateCellValue();                }                break;            default:                break;        }        return null;    }    /**     * 获取指定Sheet页的列名,列名必须在第1列,0-based     *     * @param sheet 指定Sheet页     * @return 返回List     * @throws Exception     */    public static List
getColumnOfSheet(Sheet sheet) throws Exception { // 获取列名 Row row = sheet.getRow(1); if (row == null) { throw new Exception("The Column name is invalid!"); } List
columnList = new ArrayList
(); // 遍历列名并存入List中 for (int i = 0; i < row.getLastCellNum(); i++) { Cell cell = row.getCell(i); if (cell != null) { columnList.add(cell.getStringCellValue()); } } return columnList; } /** * 将指定Sheet页的数据转化成beanMap存入List中,列名对应值从第2行开始,0-based * * @param sheet 指定Sheet页 * @return 无结果返回空List * @throws Exception */ public static List
> handleSheetToMapList(Sheet sheet) throws Exception { List
> listMap = new ArrayList<>(); // 得到Sheet里的列名 List
columnList = ReadExcelUtils.getColumnOfSheet(sheet); for (int i = 2; i < sheet.getLastRowNum() + 1; i++) { Map
beanMap = new HashMap<>(); // 得到行 Row row = sheet.getRow(i); // 遍历列 for (int j = 0; j < row.getLastCellNum(); j++) { // 获取单元格 Cell cell = row.getCell(j); if (cell != null) { // 单元格对应列名 String key = columnList.get(j); // 单元格对应值 Object value = ReadExcelUtils.getCellValue(cell.getCellType(), cell); // key-value pairs 放入map if (value != null) { beanMap.put(key, value); } } } // 将每个beanMap放入List中 if (!beanMap.isEmpty()) { listMap.add(beanMap); } } return listMap; } /** * 将WorkBook里的所有Sheet里的记录封装成beanMap放入List中。 * * @param workbook 指定WorkBook * @return 无结果返回空List * @throws Exception */ public static List
> handleWorkBookToMapList(Workbook workbook) throws Exception { List
> listMap = new ArrayList<>(); // 遍历WorkBook所有Sheet for (int i = 0; i < workbook.getNumberOfSheets(); i++) { // 获取Sheet Sheet sheet = workbook.getSheetAt(i); // 将每个Sheet页获取到的beanMap均放入listMap中 listMap.addAll(ReadExcelUtils.handleSheetToMapList(sheet)); } return listMap; } /** * 指定Excel文件里的记录封装成beanMap返回 * * @param file Excel文件 * @return 无结果返回空List * @throws Exception */ public static List
> handleWorkBookToMapList(File file) throws Exception { Workbook workbook = WorkbookFactory.create(file); List
> listMap = ReadExcelUtils.handleWorkBookToMapList(workbook); if (workbook != null) { workbook.close(); } return listMap; } /** * 将指定WorkBook里的记录封装成指定类型放入List中返回 * * @param clazz 指定封装类型 * @param workbook * @return 无结果返回空List * @throws Exception */ public static
List
handleWorkBookToBeans(Class
clazz, Workbook workbook) throws Exception { List
> listMap = ReadExcelUtils.handleWorkBookToMapList(workbook); List
beans = new ArrayList<>(); for (Map
map : listMap) { T bean = CommonUtils.toBean(map, clazz); if (bean != null) { beans.add(bean); } } return beans; } /** * 将指定Excel文件里的记录封装成指定类型放入List中返回 * * @param clazz 指定封装类型 * @param file 指定Excel文件 * @return 无结果返回空List * @throws Exception */ public static
List
handleWorkBookToBeans(Class
clazz, File file) throws Exception { Workbook workbook = WorkbookFactory.create(file); List
beans = ReadExcelUtils.handleWorkBookToBeans(clazz, workbook); if (workbook != null) { workbook.close(); } return beans; }}

3.向Excel文件中写入数据,数据量不大的情况

此种操作Excel文件的方式与上面一种相同,会在内存中吧数据全部写入Excel的WorkBook对象中,再把WorkBook写入硬盘文件中。所以适用于小数据量的情况。代码如下:

package at.flying.tools;import org.apache.commons.beanutils.PropertyUtils;import org.apache.poi.hssf.usermodel.HSSFWorkbook;import org.apache.poi.ss.usermodel.*;import org.apache.poi.xssf.usermodel.XSSFWorkbook;import java.io.*;import java.text.SimpleDateFormat;import java.util.*;/** * 本类用于将JavaBean写入Excel文档中,采用POI(3.14及以上版本)技术,使用时需导入相关Jar包。 * Created by Flying on 2016/5/28. */public class WriteExcelUtils {    public final static int XLSX = 1;    public final static int XLS = 2;    //默认日期格式    private static SimpleDateFormat format = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");    private WriteExcelUtils() {}    /**     * 给一个Cell赋值,若为空则视为"",若不为基本类型及其包装类(日期类型除外),则其值通过toString()获取     *     * @param cell       一个单元格     * @param value      值     * @param dateFormat 日期格式     * @return 返回传入的Cell     */    public static Cell setCellValue(Cell cell, Object value, String dateFormat) {        if (dateFormat != null && !dateFormat.isEmpty()) {            format.applyPattern(dateFormat);        }        if (value == null) {            cell.setCellValue("");        } else if (value instanceof String) {            cell.setCellValue((String) value);        } else if (value instanceof Integer) {            cell.setCellValue((Integer) value);        } else if (value instanceof Date) {            cell.setCellValue(format.format(value));        } else if (value instanceof Calendar) {            cell.setCellValue(format.format(value));        } else if (value instanceof Boolean) {            cell.setCellValue((Boolean) value);        } else if (value instanceof Float) {            cell.setCellValue((Float) value);        } else if (value instanceof Double) {            cell.setCellValue((Double) value);        } else {            cell.setCellValue(value.toString());        }        return cell;    }    /**     * 将一个Bean写入某一行中,bean的属性由properties决定     *     * @param row        指定行     * @param bean       指定Bean     * @param properties 指定写入的属性     * @param dateFormat 日期格式     * @param cellStyle  每一个单元格的样式     * @return 返回传入的Row     */    public static 
Row writePerRow(Row row, T bean, List
properties, String dateFormat, CellStyle cellStyle) { Map
mapBean = CommonUtils.toMap(bean); if (mapBean == null) { return row; } for (int i = 0; i < properties.size(); i++) { if (mapBean.containsKey(properties.get(i))) { Cell cell = row.createCell(i); cell.setCellStyle(cellStyle); WriteExcelUtils.setCellValue(cell, mapBean.get(properties.get(i)), dateFormat); } } return row; } /** * 向某一Sheet中写入多个Bean * * @param sheet 指定Sheet * @param _titleRow 指定写入的标题在第几行,0-based * @param begin 指定写入的Bean从第几个开始,包含该下标,0-based * @param end 指定写入的Bean从第几个结束,不包含该下标,0-based * @param beans 指定写入的Beans * @param properties 指定写入的bean的属性 * @param titles 指定写入的标题 * @param dateFormat 日期格式 * @return 返回传入的Sheet */ public static
Sheet writePerSheet(Sheet sheet, int _titleRow, int begin, int end, List
beans, List
properties, List
titles, String dateFormat) { Row titleRow = sheet.createRow(_titleRow); CellStyle cellStyle = sheet.getWorkbook().createCellStyle(); Font font = sheet.getWorkbook().createFont(); font.setBold(true); font.setFontHeightInPoints((short) 16); cellStyle.setFont(font); cellStyle.setAlignment(CellStyle.ALIGN_CENTER); cellStyle.setVerticalAlignment(CellStyle.VERTICAL_CENTER); sheet.setDefaultColumnWidth(16); for (int i = 0; i < titles.size(); i++) { Cell cell = titleRow.createCell(i); cell.setCellValue(titles.get(i)); cell.setCellStyle(cellStyle); } //单元格样式 CellStyle _cellStyle = sheet.getWorkbook().createCellStyle(); _cellStyle.setAlignment(CellStyle.ALIGN_CENTER); _cellStyle.setVerticalAlignment(CellStyle.VERTICAL_CENTER); int counter = _titleRow + 1; for (int i = begin; i < (end < beans.size() ? end : beans.size()); i++) { Row row = sheet.createRow(counter++); WriteExcelUtils.writePerRow(row, beans.get(i), properties, dateFormat, _cellStyle); } return sheet; } /** * 向某一WorkBook中写入多个Bean * * @param workbook 指定工作簿 * @param _titleRow 指定写入的标题在第几行,0-based * @param count 指定每个Sheet写入几个bean * @param beans 指定写入的Beans * @param properties 指定写入的bean的属性 * @param titles 指定写入的标题 * @param dateFormat 日期格式 * @return 返回传入的WorkBook */ public static
Workbook writeWorkBook(Workbook workbook, int _titleRow, int count, List
beans, List
properties, List
titles, String dateFormat) { int sheetNum = beans.size() % count == 0 ? beans.size() / count : (beans.size() / count + 1); for (int i = 0; i < sheetNum; i++) { WriteExcelUtils .writePerSheet(workbook.createSheet(), _titleRow, i * count, i * count + count, beans, properties, titles, dateFormat); } return workbook; } /** * 向工作簿中写入beans,标题默认从第0行开始,0-based * * @param workbook 指定工作簿 * @param count 指定每一个Sheet写入几个Bean * @param beans 指定写入的Beans * @param properties 指定写入的bean的属性 * @param titles 指定写入的标题 * @param dateFormat 日期格式 * @return 返回传入的WorkBook */ public static
Workbook writeWorkBook(Workbook workbook, int count, List
beans, List
properties, List
titles, String dateFormat) { return WriteExcelUtils.writeWorkBook(workbook, 0, count, beans, properties, titles, dateFormat); } /** * 向工作簿中写入beans,所有bean写在一个Sheet中,标题写在第0行,0-based * * @param workbook 指定工作簿 * @param beans 指定写入的Beans * @param properties 指定写入的bean的属性 * @param titles 指定写入的标题 * @param dateFormat 日期格式 * @return 返回传入的WorkBook */ public static
Workbook writeWorkBook(Workbook workbook, List
beans, List
properties, List
titles, String dateFormat) { return WriteExcelUtils.writeWorkBook(workbook, beans.size(), beans, properties, titles, dateFormat); } /** * 向工作簿中写入beans,所有bean写在一个Sheet中,标题写在第0行,0-based。并输出到指定file中 * * @param file 指定Excel输出文件 * @param excelType 输出Excel文件类型{@link #XLSX}或者{@link #XLS},此类型必须与file文件名后缀匹配 * @param beans 指定写入的Beans * @param properties 指定写入的bean的属性 * @param titles 指定写入的标题 * @param dateFormat 日期格式 * @return 返回传入的WorkBook * @throws Exception */ public static
void writeWorkBook(File file, int excelType, List
beans, List
properties, List
titles, String dateFormat) throws Exception { Workbook workbook = null; if (XLSX == excelType) { workbook = new XSSFWorkbook(); } else if (XLS == excelType) { workbook = new HSSFWorkbook(); } else { throw new Exception("excelType参数错误"); } WriteExcelUtils.writeWorkBook(workbook, beans.size(), beans, properties, titles, dateFormat); OutputStream os = null; try { os = new FileOutputStream(file); WriteExcelUtils.writeWorkBookToExcel(workbook, os); } catch (Exception e) { throw e; } finally { CommonUtils.closeIOStream(null, os); } } /** * 向工作簿中写入beans,所有bean写在一个Sheet中,默认以bean中的所有属性作为标题且写入第0行,0-based。并输出到指定file中 * * @param file 指定Excel输出文件 * @param excelType 输出Excel文件类型{@link #XLSX}或者{@link #XLS},此类型必须与file文件名后缀匹配 * @param beans 指定写入的Beans * @param dateFormat 日期格式 * @return 返回传入的WorkBook * @throws Exception */ public static
void writeWorkBook(File file, int excelType, List
beans, String dateFormat) throws Exception { if (beans == null || beans.isEmpty()) { throw new Exception("beans参数不能为空"); } Map
map = CommonUtils.toMap(beans.get(0)); if (map == null) { throw new Exception("获取bean属性失败"); } List
properties = new ArrayList<>(); properties.addAll(map.keySet()); WriteExcelUtils.writeWorkBook(file, excelType, beans, properties, properties, dateFormat); } /** * 向工作簿中写入beans,所有bean写在一个Sheet中,默认以bean中的所有属性作为标题且写入第0行,0-based。 * 日期格式采用默认类型。并输出到指定file中 * * @param file 指定Excel输出文件 * @param excelType 输出Excel文件类型{@link #XLSX}或者{@link #XLS},此类型必须与file文件名后缀匹配 * @param beans 指定写入的Beans * @return 返回传入的WorkBook * @throws Exception */ public static
void writeWorkBook(File file, int excelType, List
beans) throws Exception { WriteExcelUtils.writeWorkBook(file, excelType, beans, null); } /** * 将Beans写入WorkBook中,默认以bean中的所有属性作为标题且写入第0行,0-based * * @param workbook 指定工作簿 * @param beans 指定写入的Beans * @param dateFormat 日期格式 * @return 返回传入的WorkBook */ public static
Workbook writeWorkBook(Workbook workbook, List
beans, String dateFormat) { if (beans == null || beans.isEmpty()) { return workbook; } Map
map = CommonUtils.toMap(beans.get(0)); if (map == null) { return workbook; } List
properties = new ArrayList<>(); properties.addAll(map.keySet()); return WriteExcelUtils.writeWorkBook(workbook, beans, properties, properties, dateFormat); } /** * 将Beans写入WorkBook中,默认以bean中的所有属性作为标题且写入第0行,0-based * * @param workbook 指定工作簿 * @param beans 指定写入的Beans * @return 返回传入的WorkBook */ public static
Workbook writeWorkBook(Workbook workbook, List
beans) { return WriteExcelUtils.writeWorkBook(workbook, beans, null); } /** * 将指定WorkBook写入指定文件中 * * @param workbook 指定工作簿 * @param os 指定输出流,需要手动关闭 * @throws IOException 出现I/O异常抛出 */ public static void writeWorkBookToExcel(Workbook workbook, OutputStream os) throws IOException { workbook.write(os); }}

4.读取大数据量的Excel文档(XLSX格式)

此种方式是通过Stream流读方式读取Excel文档,故可以读取几百万行数据的Excel文档,每读指定的行数都可以调用回调函数来做相应处理,为了更好的进行数据格式的转换,修改了某些依赖类的源码。

参考代码:

代码如下:

被修改的org.apache.poi.xssf.eventusermodel.XSSFSheetXMLHandler类

(重写为XSSFSheetXMLHandlerPlus文件,在使用时需要引入此修改类,代码篇幅太长,可以查看Github上的代码):

为节省篇幅,请转看我托管在github上的代码,里面有详细注释。

类名为:com.github.flyinghe.tools.XLSXReader

回调函数接口(4,5回调函数均使用此接口):

ExcelHandler.java:

package at.flying.depdcy;import java.io.File;import java.util.List;import java.util.Map;/** * Created by FlyingHe on 2017/8/9. * 此接口为{@link at.flying.tools.XLSReader}和{@link at.flying.tools.XLSXReader}提供回调函数 */public interface ExcelHandler {    /**     * 此函数作为{@link at.flying.tools.XLSReader}和{@link at.flying.tools.XLSXReader}读取Excel文件工具类的回调函数     *     * @param currentRowInSheet   调用此函数时的当前sheet的当前行坐标,0-based     * @param currentSheetInExcel 调用此函数时的当前sheet坐标,0-based     * @param realRowInSheet      调用此函数时的当前sheet已经解析的数据的非空行数(即不包括第0行和第一行等标题行以及所有空行)     * @param realRowInExcel      调用此函数时整个Excel文档已经解析的数据的非空行数(即不包括所有sheet的第0行和第一行等标题行以及所有空行)     * @param allSheetInExcel     调用此函数时整个Excel文档已经解析的sheet数(包含正在解析的Sheet)     * @param titles              标题,即第0行数据,0-based     * @param columns             列名,即第1行数据,0-based     * @param datas               datas被清空前调用此函数时的数据行数据,     *                            在limit>0的情况下datas.size()一般等于limit     *                            (在datas未达到指定限制而文件数据已经完全读取完毕的情况下datas.size()会小于limit),     *                            注意:若没有设置limit(即limit<=0的情况下),不会调用回调函数,     *                            此时你应该使用类似于{@link at.flying.tools.XLSXReader#readExcelToMapList(File)}等等     *                            不需要提供回调函数的静态函数来做处理     * @see at.flying.tools.XLSReader#readExcelToMapList(File)     * @see at.flying.tools.XLSReader#readExcelToMapList(File, Integer)     * @see at.flying.tools.XLSReader#readExcel(File)     * @see at.flying.tools.XLSReader#readExcel(File, Integer)     */    public void callback(int currentRowInSheet, int currentSheetInExcel, int realRowInSheet, int realRowInExcel,                         int allSheetInExcel, List
titles, List
columns, List
> datas) throws Exception;}

5.读取大数据量的Excel文档(XLS格式)

此种方式同上种方式,也是通过流读方式。

参考代码:

代码如下:

被修改的org.apache.poi.hssf.eventusermodel.FormatTrackingHSSFListener类

(重写为FormatTrackingHSSFListenerPlus文件,在使用时需要引入此修改类,代码篇幅太长,可以查看Github上的代码)。

为节省篇幅,请转看我托管在github上的代码,里面有详细注释。

类名为:com.github.flyinghe.tools.XLSReader

 

6.向Excel文档中写入大量数据

向Excel文档中写入大量数据,查阅官方文档貌似仅支持XLSX后缀的。

参考官方文档:

为节省篇幅,请转看我托管在github上的代码,里面有详细注释。

类名为:com.github.flyinghe.tools.XLSXWriter

注意:

此Jar包也已经上传到Maven中央仓库,你可以使用Maven依赖直接引用:

 

com.github.flyinghe
common-utils
2.3.3-RELEASE

读取Excel文件时对文件格式有一定要求,特别是读取之后将数据封装到Map或者JavaBean中时,Excel中每一个Sheet的格式如下:

174004_Mr8n_2608182.png

转载于:https://my.oschina.net/u/2608182/blog/1507447

你可能感兴趣的文章
String,StringBuffer,StringBuilder的整理
查看>>
mysql 字符截取 实列
查看>>
部署mysql高可用、读写分离集群
查看>>
jquery中下拉多选插件jquery.multiSelect的使用
查看>>
梦想与现实,你会选择什么——一个电子爱好者的迷茫
查看>>
用rabbitMQ实现生产者消费者
查看>>
GRADLE遇见“设备未就绪”
查看>>
正则表达式实现——匹配括号中的A 以及 匹配非括号中的A
查看>>
golang锁sync.Mutex
查看>>
定义自己的JSTL标签库
查看>>
JAVA 相关
查看>>
PowerDesigner导出数据结构到word
查看>>
性能调优--永远超乎想象
查看>>
zabbix监控mysql
查看>>
cisco路由器上做端口映射
查看>>
Lua 和 C/C++ 互相调用实例分析
查看>>
初见:存储过程 调用方法
查看>>
centos 6.7 安装注意事项
查看>>
网络安全求职指南
查看>>
mysql mmm
查看>>