先来回顾下通常把java对Excel的操作分为以下功能:1、生成模板,导出模板;2、填充模板,录入数据;3;读取数据库数据,导出数据;在上一篇博文中,我简单记录了模板生成和导出,在这篇博文中,主要来记录--Excel文件导入,数据录入(仍然是以jsp+servlet为例)
既然要解决这个问题,那首先来分析下我们需要面对的有哪些需求需要实现:
1、Excel文件导入(这是最基础的,巧妇难为无米之炊,导入环节也是查了好久才完成的);
2、Excel文件中数据的格式判定,你要读取文件,如果文件中其实没有数据怎么办,程序会报错么;
3、Excel文件中数据的数值判定,“编号”啊,“身份证号”啊之类是不是需要有一定的规范才说明数值是正确的,否则录入毫无意义,数据库中可能还会报错;
4、Excel文件中数据判定完成后,判定全部正确的,全部录入数据库,录入成功显示录入成功,录入不成功则修改录入环节;
判定如果存在错误,将所有出现错误的列汇总,返回界面提示所有出错的列;
首先,我们来完成导入功能,实现如下效果:
相信这个上传效果,很多人都能实现,<input type="file">如是而已嘛,但是实现它后我们如何进行excel数据操作呢?通常我们想到的有如下两种方法:
1、将excel文件传上去,然后对传上的文件进行操作,因为我们传到哪了我们知道,可以直接获取路径;
2、我们可以直接获取想要上传的文件在电脑上的路径,然后我们通过路径直接对文件进行操作;
这里我主要来介绍下我实现的第二种方法,(以ie浏览器为例,其它浏览器暂不讨论)
1 function upLoad(){ 2 var myFile=document.getElementById("myFile"); 3 myFile.select(); 4 var realPath=document.selection.createRange().text; 5 var len=realPath.length; 6 var path=realPath.substr(len-4); 7 if(path==".xls"){
document.getElementById("myForm").action="upLoad?path="+realPath; 8 document.getElementById("myForm").submit(); 9 }else{ 10 alert("请输入excel格式的文件"); 11 } 12 }
通常情况下,在ie7之前我们可以通过document.getElementById('file_upl').value 直接获取文本的本地路径,但是在ie8之后,处于安全性考虑,上传时获取以上value值则会以“C:\fakepath\”来代替了,这个时候我们就需要 上文中出现的var myFile=document.getElementById("myFile"); myFile.select();var realPath=document.selection.createRange().text;这三步来进行完成了。
这样在ie浏览器下就可以获取到文件的本地路径了,从而继续接下来的文本读取工作。
其次,我们来解决接下来的问题,既然找到文件了,那就开始对文件进行操作,
(这里我们只考虑,对某个单个文件的直接读取,不考虑多文件优化等问题)我们通过如下步骤来进行数据读取:
a、java对excel的数据读取,原理上是将excel上的数据复制到hssfworkbook工作簿上,然后对工作簿进行操作
1 File file=new FileInputStream(String path) 2 BufferedInputStream bis=new BufferedInputStream(file); 3 POIFSFileSystem pfs=new POIFSFileSystem(bis);
b、复制完成后就是对hssfworkbook工作簿上数据的处理,是从页、行、列依次进行开始读取
1 //每一页每一页的进行数据处理 2 HSSFWorkbook book=new HSSFWorkbook(pfs); 3 for (int k = 0; k < book.getNumberOfSheets(); k++) { 4 HSSFSheet sheet=book.createSheet(); 5 //每一行每一行的进行处理 6 for (int i = 0; i < sheet.getLastRowNum(); i++) { 7 HSSFRow row=sheet.createRow(i); 8 if(row==null){continue;} 9 String[] values = new String[i]; 10 Arrays.fill(values, ""); 11 for (int j = 0; j < row.getLastCellNum()+1; j++) { 12 String value=""; 13 HSSFCell cell=row.createCell(j);
c、在进行每一个单元格读取的时候,我们需要根据不同的数据,用不同的获取方法,避免读取过程出现异常
1 switch (cell.getCellType()) { 2 case HSSFCell.CELL_TYPE_STRING: 3 value = cell.getStringCellValue(); 4 break; 5 case HSSFCell.CELL_TYPE_NUMERIC: 6 if (HSSFDateUtil.isCellDateFormatted(cell)) { 7 Date date = cell.getDateCellValue(); 8 if (date != null) { 9 value = new SimpleDateFormat("yyyy-MM-dd").format(date); 10 } else { 11 value = ""; 12 } 13 } else { 14 value = newDecimalFormat("0").format(cell.getNumericCellValue()); 15 } 16 break; 17 case HSSFCell.CELL_TYPE_FORMULA: 18 // 导入时如果为公式生成的数据则无值 19 if (!cell.getStringCellValue().equals("")) { 20 value = cell.getStringCellValue(); 21 } else { 22 value = cell.getNumericCellValue() + ""; 23 } 24 break; 25 case HSSFCell.CELL_TYPE_BLANK: 26 value=""; 27 break; 28 case HSSFCell.CELL_TYPE_ERROR: 29 value = ""; 30 break; 31 case HSSFCell.CELL_TYPE_BOOLEAN: 32 value = (cell.getBooleanCellValue() == true ? "Y":"N"); 33 break; 34 default: 35 value = ""; 36 }
如上是对不同数据类型不同的读取,java的poi读取中,通常用到的数据类型为:
CELL_TYPE_BLANK 空值
CELL_TYPE_BOOLEAN 布尔型
CELL_TYPE_ERROR 错误
CELL_TYPE_FORMULA 公式型
CELL_TYPE_STRING 字符串型
CELL_TYPE_NUMERIC 数值型
d、接下来就是对于不同的数据判定结果,进行的操作不同,在这里我们判定如果正确列直接录入数据库,如果不正确的则返回界面
(当然实际应用中应当是全部正确才能录入,如果存在不正确的数据,整个文件都不能录入)
1 if (i == 0 && value.trim().equals("")) { 2 break; 3 } 4 values[i] = value; 5 } 6 } 7 } 8 } 9 //进行判定全局变量boolean值all,如果为true,则说明已经数据完全正确,则取值向数据库中进行存取,如果为false则存在错误信息,将得到的错误信息直接返回,不再向数据库发送存取请求 10 } 11 return returnArray; 12 }
最后面这部分写的略微有些凌乱,这样我们来回顾一下读取步骤:
1、获取文件地址;
2、根据文件地址,读取文件;
(这里应该是读取全部数据,可以先进行基本数据的判定,如果存在不对的数据类型,则记录并返回提示,如果正确了再进行下一步);
3、如果判定之前的数据类型设定都正确了,则进行判定数据的具体规格,例如:编号前几位必须为http,身份证号码共18位等;
4、如果以上所有的判定都满足,则将数据进行录入数据库操作,(数据录入数据库,相信都不会陌生了吧)
如果存在问题,则应该返回界面进行相应的提示操作。