要用Java POI读取Exce从xml文件中读取数据的数据,并且实现对数据的格式校验,输入错误信息

注意:此参考解决方案只是针对xlsx格式的excel文件!
& & 前一段时间遇到一种情况,服务器经常宕机,而且没有规律性,查看GC日志发生了out of memory,是堆溢出导致的,分析了一下堆的dump文件,发现在发生OOM时创建了大量的String对象。最后对照时间点,发现宕机的时候业务人员在上传一个excel文件,但是这个excel文件才28MB大小,感觉应该不会引起内存溢出。后来在本地启动了服务,然后尝试上传这个excel文件,同时使用Java VisualVM监控GC情况,发现在上传的时候,创建了大量的String对象,后来老年代没有可分配空间导致了OOM。最终分析结果是,excel文件中存在几十万的空行数据,表面上看,这些空行数据跟不存在数据的行是一样的,但是POI会把这种空行数据读入到内存中,感觉这也是一个坑。
& & 在网上搜了很长时间,发现国内网站上的解决方案真是没法看,基本上答案都差不多,没有什么有见解性的解决方法,后来在stackoverflow上找到了解决方法。算是给自己做一下备注,也想帮助一些还在坑里的人,就分享一下,只是自己的见解,有不得当的地方也请见谅。
常规读取方法
& & 通常在读取excel文件时(.xlsx),是使用如下代码进行加载的:
FileInputStream fi = new FileInputStream("e:/2.xlsx");
XSSFWorkbook wk = new XSSFWorkbook(fi);
& & 然后再获取对应的Sheet、Row和Cell,然后获取excel中的内容,但是这种方式POI会把文件的所有内容都加载到内存中,读取大的excel文件时很容易占用大量内存。
尝试解决方法
& & 使用Excel Streaming Reader,这个第三方工具会把一部分的行(可以设置)缓存到内存中,在迭代时不断加载行到内存中,而不是一次性的加载所有记录到内存,这样就可以不断的读取excel内容并且不影响内存的使用。
& & 但是这个工具也有一定的限制:只能用于读取excel的内容,写入操作不可用;可以使用getSheetAt()方法获取到对应的Sheet,因为当前只是加载了有限的row在内存中,因此不能随机访问row,即不能使用getRow(int rowNum)方法;由于行数据已经加载到了内存,因此可以随机的访问Cell数据,即可以使用getCell(int cellnum)方法。使用这个工具,建议使用迭代器来进行迭代。具体内容可以参见:/monitorjbl/excel-streaming-reader。
& & 在pom.xml文件中引入需要的jar包:
&dependency&
&groupId&com.monitorjbl&/groupId&
&artifactId&xlsx-streamer&/artifactId&
&version&1.2.0&/version&
&/dependency&
& & 使用代码如下:
public void testLoad() throws Exception{
FileInputStream in = new FileInputStream("e:/2.xlsx");
Workbook wk = StreamingReader.builder()
.rowCacheSize(100)
//缓存到内存中的行数,默认是10
.bufferSize(4096)
//读取资源时,缓存到内存的字节大小,默认是1024
.open(in);
//打开资源,必须,可以是InputStream或者是File,注意:只能打开XLSX格式的文件
Sheet sheet = wk.getSheetAt(0);
//遍历所有的行
for (Row row : sheet) {
System.out.println("开始遍历第" + row.getRowNum() + "行数据:");
//遍历所有的列
for (Cell cell : row) {
System.out.print(cell.getStringCellValue() + " ");
System.out.println(" ");
参考资料:/questions//how-to-load-a-large-xlsx-file-with-apache-poi
阅读(...) 评论()在Poi读取Excel数据的过程当中,经常会因为用户操作的问题或是Excel的自动转换将单元格数据转换为科学计数法形式表现,那么这时在后台读取数据并进行校验数据格式的过程当中有可能就会出现错误,或者是出现一些意想不到的问题,那现在就需要对读取的Excel数据进行转换,使用java的DecimalFormat 进行格式化,示例如下:
  DecimalFormat df = new DecimalFormat(&0&);//使用DecimalFormat类对科学计数法格式的数字进行格式化
&&&&contents = df.format(cell.getNumericCellValue());
对于读取到的单元格内容暂时还不能转换为字符串,这样会转换不过去,而报错。
转载注明本文地址:&
&&相关文章推荐
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:2343次
排名:千里之外
(2)(2)(1)(2)POI 读取excel文件的数据,怎么判断该行是否为空?【java吧】_百度贴吧
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&签到排名:今日本吧第个签到,本吧因你更精彩,明天继续来努力!
本吧签到人数:0成为超级会员,使用一键签到本月漏签0次!成为超级会员,赠送8张补签卡连续签到:天&&累计签到:天超级会员单次开通12个月以上,赠送连续签到卡3张
关注:636,023贴子:
POI 读取excel文件的数据,怎么判断该行是否为空?收藏
问题:excel中的下边为空白的数据再密码行读取时还是会获取到对象,但是该对象的stringValue值为null,怎么才能判断下边的一行是否为空,如果为空就不去记录数据,按照目前代码解决的方法,下边的看着为空的数据确实不会计入统计总数,但是上边有数据,没有密码的数据也会被忽略,求解决方法,大神在哪里?
java,亚马逊网上书城,满59元免运费,好书不间断!700多万种中外正版精品图书一网打尽!买java,就上Z.CN!正版图书,天天低价特惠,让您挚爱阅读!
求大神解答
我记得如果一个row没有做任何修改(那行没有任何数据),HSSFRow
row = sheet.getRow(index);index超出了Excel数据范围,是要抛异常的。这只是我记得哈,莫要当真
登录百度帐号推荐应用Excel表格导入,并且对导入的数据做验证,成功的进行导入,失败的不导入,各位大大有什么好的建议! - ITeye问答
1.Excel表格导入,并且对导入的数据做验证,成功的进行导入,失败的不导入,各位大大有什么好的建议!2.页面上并且有覆盖与追加的按钮,覆盖是指如果数据库中存在code唯一值则更新,否则插入;追加是值如果存在则不操作,不存在则插入。还有一个选择表名的下拉框,选择录入不同的表。3.因为要实现不同的表的数据导入,并且表中的外键(数据库没设置)必须用中文从别的表取得Id录入!!4.验证后的数据并不是直接录入数据库,而是先在页面显示,用户在提交录入!
现在小弟的实现方法是将数据录入到List&String[]&中,然后用一个验证数据库表对每一个表格进行验证,增加个错误提示,然后对正确的存在另一个List&String[]&中,但是在这里要存入数据库就有点问题了,因为List中存放的数据格式都是字符串String,要存入数据库中不好操作(因为录入的表格动态操作,所以不清楚到底会有多少列)!
项目用的框架是JSF+ibatis+Spring的,大家有没有更好的方法……
最后一点,写入有点问题,更正一下。
--------------------------------------------------------------
最后补充一点:读取数据时,需要注意设置读取范围,比如限制范围为A2 -Z10,否则,如果Excel表格的某一行或某一列被加了背景颜色,你会发现,他会读取很多空数据,由于你加背景,jxl或pio会认为他是有效的。因此都会读取出来。
之前也做过一个类似的功能,由于校验需求优先级不够高且手头工作量比较大,因此数据校验这块没做。
提供一些思路,仅供参考。
首先你要不要考虑你导入数据的本身的情况
1.你的源数据是FreeForm,还是DataGrid甚至两种情况都存在,当然,建议是两种格式的数据导入均有实现。
2.通常情况下,导入的数据不太可能大得太离谱,因此可考虑定义一个java数据集模型,先批量把这批数据读入数据模集型,而后考虑检验及写入数据库。因此导入流程为:
读取数据 --& 进入数据集模型 --& 进行校验 --& 写入数据库
3.有了数据模型之后,可以页面显示同时把数据集模型序列化至一个临时文件,用户在提交时,反序列化模型,同时,调用写入数据库操作写入数据库。
由于本人不太清楚你那里有些什么校验。现将校验分一下类:
1.是否允许空值
2.正则表达式校验,如年龄,金额,日期等
3.逻辑校验,如要求:B1==C1+D1,A2==B2+C3
4.其他暂时考虑不到的校验可考虑抽出接口,具体情况具体实现。
最后补充一点:读取数据时,需要注意设置读取范围,限制范围为A1 -Z10,否则,如果Excel表格的某一行或某一列被加了背景颜色,你会发现,他会读取很多空数据,浪费资源。
使用POI操作Excel非常方便
上个月做了一个功能,和楼主描述的差不多。
针对你提出问题(1):我是用了jxl和poi两个支持包,把需要导入的。
数据字段XML配置好,然后用jodm解析的。因我哪里需求有两种,一是只有有错误,整个表算入失败,另生成一个excel表,用颜色标记 错误行,并给出错误提示信息。另外只导出错误数据也用颜色标记 错误行,并给出错误提示信息。
问题(2):导出的表名,我从数据库加载的,用户在导入之前,先要下载我准备好的模板。你说覆盖好实现,因为你如果用的是poi这个包话,一般是把要导入的数据把一个二维数组里的。可以拿来比例。(因我哪里,每月只保存一条有效记录,所以在导入之前我都清空表数据。当然是放到事务里执行的)。
问题(3):可在以xml中定义我哪里这么定义的
&table name="tablename"&
&column name="code" type="string" index="0" des="代号"&
&/column
...
&/table&
你根据需求可以自己定义,在java中用jodm解析就可以了。
问题(4):这个更好解决,在你拼insert()时候,用事务控制,还有很多其它办法。
今天用的公司电脑,没有代码需要的话的Q我。
没有那么复杂吧,既然你不知道有多少列,那就别用List&String[]&,直接用List&List&String&&,建议用Vector&Vector&String&&呵呵。
这么多问题,还是要找到问题的根本。我觉得,当前问题的根本不是excel导入的问题。而是读取的数据,不好存入数据库的问题。
首先,每次导入时选的表不同,导致数据列不同。这个其实还比较简单。有两种方案可以选择。
1. 查询数据库数据字典,可以根据表名,查询到表中各字段的名字及数据类型。在存入数据时,采用拼SQL,批量存储的方式。 这里有一个关键问题是excel中的列与数据库表中字段名的映射问题,只要这个问题解决了,就好办。解决这个问题应该不难。缺点,数据库表中的所有字段都会被读取出来,进行操作,有时不符合需求。
2。 数据库表的结构相对是稳定的。可以在数据库中增加一张表,来记录,哪些表需要数据导入,各个数据表哪些字段需要导入,及各字段数据类型。(这里记录的数据类型可以是java类型以方便直接数据类型转换,也可以是数据库列数据类型。)。这个好处是可以灵活控制导入的字段的范围。缺点:有表增加时,需要手动增加数据记录来记录配置。
我们项目中用到的excel导入是用的apache poi框架,这个目前来看,还不错,它的文档也很详细,excel文件的内容会被读取到sheet页中,直接获取sheet页中有多少row,就知道当前录入的excel文件中有多少条数据了
看这个类似的设计文档
http://trent-/blog/1074807
JDBC中用setString(index,value)可以存oracle中的任何数据类型,但构造动态数据时,要参考具体的数据库的特征,我用的是oracle,内部主键由序列产生,所以传参的时候会增加序列串名,日期格式的也需要增加oracle内部的todate()函数的串名,形式如下:
strBuf.append(tablename);
strBuf.append("_SEQUENCE.NEXTVAL");
strBuf.append("TO_DATE(SUBSTR(?,1,19),'YYYY-MM-DD HH24:MI:SS'),");
sql = "INSERT INTO " + tableName
+ "(" + strBufFN.toString()
+ " VALUES ("+ strBufFV.toString() + ")";
我做的和你类似,是一个TXT文本导入到数据库,我把页面表单输入的数据库表名,以及数据库表字段和文本文件的数据项之间的对应关系,导入方式等都记录到XML配置文件中,然后根据该配置文件导入数据。数据导入的语句是用预处理动态生成的,形式如:
INSERT INO TABLE_NAME(field1,field2,...,fieldn) values(?,?,...,?)用StringBuffer构造这种预处理语句,然后批量提交;当页面选择数据库表的时候,用Ajax动态刷新数据库表字段和文件数据项的对应关系,用户设置完成后点保存就可以自动生成配置文件,导入的时候读这个配置文件就可以了
直接读取Excel的值插入到数据库表里,操作excel的包可以知道你有多少列的,所以你只要动态的创建表,吧数据存在表里,然后预留两个字段,分别记录是否成功,以及成功和失败的备注,更新结束后,你建的表中成功的更新为成功,错误的更新错误原因。。最后再把这张表的数据导出Excel即可。。
如果用flex的话还好做一点,如果用jsp,这个要做无刷新上传吗?
public String saveLoan(){
String function="借用";
User user=(User)getSession().getAttribute("user");
credenceNum=RandomUtils.getBicker(user.getCompanyName(),function);
if(upload!=null && uploadFileName !=null){
@SuppressWarnings("unused")
String project="";
@SuppressWarnings("unused")
String projectname="";
@SuppressWarnings("unused")
String filename="";
getResponse().setContentType("text/charset=UTF-8");
getRequest().setCharacterEncoding("UTF-8");
System.out.println(getUpload());
System.out.println(getUploadFileName());
String rootPath=getSession().getServletContext().getRealPath("/");
System.out.println(rootPath);
String newFileName=MyUtils.upload(getUploadFileName(), rootPath, getUpload());
System.out.println(newFileName);
FileInputStream excelResourcr=new FileInputStream(rootPath+newFileName);
Workbook rwb=Workbook.getWorkbook(excelResourcr);
@SuppressWarnings("unused")
int sheetCount=rwb.getNumberOfSheets();
Sheet rs=rwb.getSheet(0);
//Sheet rs1=rwb.getSheet(1);
int rows=rs.getRows();
int clos=rs.getColumns();
//在JXL中却恰恰相反,是“先列后行”
loan=new Loan();
loan.setSerialnum(credenceNum);
loan.setFeedmanager(user.getEmplName());
loan.setFeedmanagertel(user.getMobilePhone());
loan.setBorrowaddress(user.getCompanyName());
loan.setAction("0010");
//System.out.println(user.getEmplNo());
loan.setState("1");
int getokCount=0;
for(int i=0;i&rows-1;i++){
for(int n=0;n&n++){
loan.setRowNum(i+1);
switch(n){
//loan.setBorrowdate(null);
loan.setTempborrowdate(rs.getCell(0,i+1).getContents());
///System.out.println(rs.getCell(1,i+1).getContents());
loan.setBorrowname(rs.getCell(1,i+1).getContents());
loan.setBorrownum(rs.getCell(2,i+1).getContents());
loan.setDepartment(rs.getCell(3,i+1).getContents());
&&&&&&&&&&&&&&&&&&&&&&& loan.setDept(rs.getCell(4,i+1).getContents());
&&&&&&&&&&&&&&&&&&&&&&&
loan.setFeednum(rs.getCell(5,i+1).getContents());
loan.setDepict(rs.getCell(6,i+1).getContents());
loan.setFeedtype((rs.getCell(7,i+1).getContents()));
//System.out.println(rs.getCell(7,i+2).getContents());
loan.setItemname(rs.getCell(8,i+1).getContents());
loan.setItemmoment(rs.getCell(9,i+1).getContents());
loan.setOutstore(rs.getCell(11,i+1).getContents());
loan.setBorrowscalar(rs.getCell(12,i+1).getContents());
loan.setUnit(rs.getCell(13,i+1).getContents());
loan.setOutstone(rs.getCell(14,i+1).getContents());
loan.setBorrowterm(rs.getCell(15,i+1).getContents());
loan.setBorrowemail(rs.getCell(16,i+1).getContents());
loan.setBorrowmobile(rs.getCell(17,i+1).getContents());
loan.setBorrowtel(rs.getCell(18,i+1).getContents());
loan.setUsetype(rs.getCell(19,i+1).getContents());
loan.setMovereason(rs.getCell(20,i+1).getContents());
loan.setReamark(rs.getCell(21, i+1).getContents());
&&& reMesage =(String)loanService.saveLoan(loan);
//System.out.println(reMesage);
if("OK".equals(reMesage)){
getokCount=getokCount+1;
rwb.close();
File file=new File(rootPath+newFileName);
if(file.exists()){
file.delete();
int errorcount=rows-1-getokC
if (getokCount==rows-1){
//this.jsonString = "{success:true,contentTypeIsValid:" + getUploadFileName() + "}";
this.jsonString = "{success:true,credenceNum:'"+credenceNum+"'}";
getRequest().setAttribute("jsonString", jsonString);
return SUCCESS;
}else if (getokCount!=0 && getokCount!=rows-1){
this.jsonString = "{success:true,credenceNum:'"+credenceNum+",失败"+ errorcount+"条记录,成功"+ getokCount +"条记录'}";
getRequest().setAttribute("jsonString", jsonString);
return SUCCESS;
}else if(getokCount==0){
this.jsonString = "{success:false,reMesage:'所有操作失败,请查看日志!'}";
getRequest().setAttribute("jsonString", jsonString);
return INPUT;
}catch(Exception e){
e.printStackTrace();
//this.jsonString = "{success:false,contentTypeIsValid:" + getUploadFileName() + "}";
this.jsonString = "{success:false,reMesage:'"+reMesage+"'}";
getRequest().setAttribute("jsonString", jsonString);
return INPUT;
直接用sql的方式操作excel应该就比较简单了吧?找找jdbc excel
引用因为录入的表格动态操作,所以不清楚到底会有多少列
那等于说是程序并不能知道有哪些列,只知道是字符串的
已解决问题
未解决问题}

我要回帖

更多关于 从文件中读取数据 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信