java poi对同一个excel文件进行读写

1 篇文章 0 订阅

环境

操作系统:win7
jdk: 1.7

需求

1、产品部的同事经常给我一批账号,然后去统计这些账号的登录次数。
他们一般给我这样的一份文件:
这里写图片描述

①我一般的做法就是先把这个账号全部读出来,用set集合装好后,再去统计登录次数。
②把统计好的结果导出到新的excel中。

关键上面第二步,产品部的人,不得不手动的重复复制粘贴操作。时间长了,他们就有意见啦。

改进

以前我总是导出到新的excel中,是因为我不知道怎么对同一份excel进行读写。
所以我特意研究下,原来那么简单。

先上代码:


try {
//连接数据库 start
MongoCredential credential = MongoCredential.createMongoCRCredential("gg_openapi", "gg_openapi", "gg..openapi#!".toCharArray());
ServerAddress serverAddress = new ServerAddress("106.75.51.20", 35520);
List<ServerAddress> addrs = new ArrayList<ServerAddress>();  
      addrs.add(serverAddress);
      List<MongoCredential> credentials = new ArrayList<MongoCredential>();
      credentials.add(credential);
      MongoClient mongoClient = new MongoClient(addrs, credentials);
      //连接数据库 end
    DB db = mongoClient.getDB("gg_openapi");

    DBCollection useropRecord = db.getCollection("userop_record");


File file = new File("C:\\Users\\yutao\\Desktop\\需要统计的报表0508.xlsx");
Set<String> accountNameSet = ExcelPoiCommon.getAccountNameSet(0, 2, file);
// 上面accountNameSet 就获取到了要统计的账号,上面我写成公共方法,下面会给出代码。
//统计登录次数 start
BasicDBObject match = new BasicDBObject();

match.append("type", new BasicDBObject("$in", new Object[]{0,1,2}));
match.append("code", "S2_l09");
match.append("account_name", new BasicDBObject("$in", accountNameSet));
BasicDBObject timeQuery = new BasicDBObject();
timeQuery.append("$lte", DateUtil.stringToDate("2016-12-31", "yyyy-MM-dd"));
timeQuery.append("$gte", DateUtil.stringToDate("2016-03-01", "yyyy-MM-dd"));
match.append("createtime", timeQuery);

            BasicDBObject group = new BasicDBObject();
            group.append("_id", "$account_name");
            group.append("loginCount", new BasicDBObject("$sum", 1));

            AggregationOutput output = useropRecord.aggregate(new BasicDBObject("$match", match), 
                                   new BasicDBObject("$group", group));

            Iterator<DBObject> iterator = output.results().iterator();

            Map<String, Object> countMap = new HashMap<String, Object>();
            while(iterator.hasNext()){
                DBObject o = iterator.next();
                countMap.put(o.get("_id").toString(), o.get("loginCount"));
            }
            //统计登录次数 end 得到了 countMap key:账号,value:次数
            //导出到同一份excel
            ExcelPoiCommon.readWriteExcel(file, countMap, 0, 2, 5);
        }catch (IOException e) {
            e.printStackTrace();
        }

①上面连接的数据库是mongodb库,版本是2.4

ExcelPoiCommon.getAccountNameSet(0, 2, file);代码如下:

/**
     * 获得文件中account_name的集合
     * 
     * @param sheeetInt excel表中哪个sheet(从0开始)
     * @param cellInt excel表中哪一列开始读(从0开始)
     * 
     * @author yutao
     * @return 
     * @throws FileNotFoundException 
     * @date 2016年12月9日下午4:14:50
     */
    public static Set<String> getAccountNameSet(int sheetInt, int cellInt, File file) throws FileNotFoundException {
        if(file == null || !file.isFile()){
            throw new FileNotFoundException("文件名不能为空或文件的后缀格式不对");
        }
        FileInputStream in;
        Set<String> accountNameSet = new HashSet<String>();
        try {
            String name = file.getName();
            name = name.substring(name.lastIndexOf(".")+1);
            in = new FileInputStream(file);
            Workbook workbookIn = null;
            Sheet sheetAt = null;
            if("xlsx".equals(name)){
                workbookIn = new XSSFWorkbook(in);
                sheetAt = workbookIn.getSheetAt(sheetInt);
                System.out.println(sheetAt.getSheetName());
            }else if("xls".equals(name)){
                workbookIn = new HSSFWorkbook(in);
                sheetAt = workbookIn.getSheetAt(sheetInt);
            }else{
                throw new FileFormatException("文件的后缀格式不对");
            }

            int firstRowNum = sheetAt.getFirstRowNum();
            int lastRowNum = sheetAt.getLastRowNum();
            // 拿到所有的账号
            for (int i = firstRowNum + 1; i <= lastRowNum; i++) {
                Row row = sheetAt.getRow(i);
                Cell cell = row.getCell(cellInt);
                if(cell == null){
                    continue;
                }
                String cellValue = cell.getStringCellValue().trim();
                accountNameSet.add(cellValue);
            }
            workbookIn.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return accountNameSet;
    }

ExcelPoiCommon.readWriteExcel(file, countMap, 0, 2, 5);代码如下:

/**
     * 读写同一份excel文件
     * @param file 需要读写的文件
     * @param countMap 统计的map集合(key:账号,value:次数)
     * @param sheetNum 读取哪个sheet(从0开始数)
     * @param cellNum 读哪一列(从0开始数)
     * @param writeCellNum 写入到哪一列(从0开始数)
     * @author yutao
     * @date 2017年5月9日下午2:50:45
     */
    public static void readWriteExcel(File file, Map<String, Object> countMap, int sheetNum, int cellNum, int writeCellNum){
        FileInputStream in;
        String name = file.getName();
        name = name.substring(name.lastIndexOf(".")+1);
        try {
            in = new FileInputStream(file);
            Workbook workbookIn = null;
            Sheet sheetAt = null;
            if("xlsx".equals(name)){
                workbookIn = new XSSFWorkbook(in);
                sheetAt = workbookIn.getSheetAt(sheetNum);
            }else if("xls".equals(name)){
                workbookIn = new HSSFWorkbook(in);
                sheetAt = workbookIn.getSheetAt(sheetNum);
            }else{
                throw new FileFormatException("文件的后缀格式不对");
            }
            System.out.println("----readWriteExcel---sheet---" + sheetAt.getSheetName());

            int firstRowNum = sheetAt.getFirstRowNum();
            int lastRowNum = sheetAt.getLastRowNum();
            // 根据账号、写入统计次数
            for (int i = firstRowNum + 1; i <= lastRowNum; i++) {
                Row rowReload = sheetAt.getRow(i);
                Cell cellReload = rowReload.getCell(cellNum);
                if(cellReload == null){
                    continue;
                }
                String cellValue = cellReload.getStringCellValue().trim();
                Cell cell5 = rowReload.getCell(writeCellNum);
                if(cell5 ==null){
                    cell5 = rowReload.createCell(writeCellNum);
                }
cell5.setCellValue(countMap.get(cellValue)==null?0:Integer.valueOf(countMap.get(cellValue).toString()));
            }
            in.close();
            FileOutputStream out = new FileOutputStream(file);
            workbookIn.write(out);
            workbookIn.close();
            out.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

总结

区别:

以前导出到新的excel我是这么写的:

XSSFWorkbook workbook = new XSSFWorkbook();

而现在是:

workbookIn = new XSSFWorkbook(in);

剩下的就OK啦!

目前处理Excel的开源javaAPI主要有两种,一是Jxl(Java Excel API),Jxl只支持Excel2003以下的版本。另外一种是Apache的Jakarta POI,相比于Jxl,POI对微软办公文档的支持更加强大,但是它使用复杂,上手慢。POI可支持更高的Excel版本2007。对Excel的读取,POI有两种模式,一是用户模式,这种方式同Jxl的使用很类似,使用简单,都是将文件一次性读到内存,文件小的时候,没有什么问题,当文件大的时候,就会出现OutOfMemory的内存溢出问题。第二种是事件驱动模式,拿Excel2007来说,其内容采用XML的格式来存储,所以处理excel就是解析XML,而目前使用事件驱动模式解析XML的API是SAX(Simple API for XML),这种模型在读取XML文档时,并没有将整个文档读入内存,而是按顺序将整个文档解析完,在解析过程中,会主动产生事件交给程序中相应的处理函数来处理当前内容。因此这种方式对系统资源要求不高,可以处理海量数据。笔者曾经做过测试,这种方法处理一千万条,每条五列的数据花费大约11分钟。可见处理海量数据的文件事件驱动是一个很好的方式。而本文中用到的AbstractExcel2003Reader、AbstractExcel2007Reader对Excel的读取都是采用这种POI的事件驱动模式。至于Excel的写操作,对较高版本的Excel2007,POI提供了很好的支持,主要流程是第一步构建工作薄和电子表格对象,第二步在一个流中构建文本文件,第三步使用流中产生的数据替换模板中的电子表格。这种方式也可以处理海量数据文件。AbstractExcel2007Writer就是使用这种方式进行写操作。对于写入较低版本的Excel2003,POI使用了用户模式来处理,就是将整个文档加载进内存,如果数据量大的话就会出现内存溢出的问题,Excel2003Writer就是使用这种方式。据笔者的测试,如果数据量大于3万条,每条8列的话,就会报OutOfMemory的错误。Excel2003中每个电子表格的记录数必须在65536以下,否则就会发生异常。目前还没有好的解决方案,建议对于海量数据写入操作,尽量使用Excel2007。
评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

山鬼谣me

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值