前面有写一篇叫做“JS获取Excel数据并显示到页面(IE)”的博客,主要讲IE环境下获取Excel数据进行显示。最近研究了下JXL,想着用JXL读取Excel数据显示到页面上,就综合了上篇弄了个综合的。
实现的功能还是从Excel中读取数据显示到页面上,不同的是添加了对其他浏览器的支持。IE当中使用ActiveXObject导数据是毋庸置疑的,火狐里面则使用JXL读取Excel数据拼接HTML显示到页面上。需要说明的是:火狐处于安全性考虑,不能获取到“浏览”文件的物理路径,也就不能通过指定文件路径来读取相应的文档。因此这里的处理方式是先上传到服务器,然后从服务器读取Excel数据。闲话说到这,上代码。
1、页面布局(与上一篇文档的布局差不多,不同的是加了个form表单):
<body> <form action="<%=path%>/ReadExcelServlet" method="post" id="fileForm" enctype="multipart/form-data"> <div class="file-box"> <input type='text' name='textfield' id='textfield' class='systext' /> <!--文本框--> <input type='button' class='but uploadbtn' value='浏览...' /><!--浏览按钮--> <input type="file" class="file" size="28" onchange="ReadExcel();" id="upfile" name="uploadFile"/> <!--真正的浏览按钮,用来打开上传页面,前面的“浏览”按钮掩盖此按钮,为好看--> </div> </form> <br /> <div id="show"></div> </body>
2、基本样式(也与上一篇文档差不多,就加了个tabFirstTr类):
<style type="text/css"> body{ background:none;} .p_show{ border-collapse:collapse; } .p_show td{ border:1px solid #ccc; text-align:center; padding-left:10px; padding-right:10px; white-space:nowrap;height:26px; line-height:26px; } .p_show th{background-color:#e1e1e1; border:1px solid #ccc; text-align:center; padding-left:10px; padding-right:10px; white-space:nowrap;height:26px; line-height:26px; } .systext{width:238px; height:28px; line-height:28px; float:left; border:1px solid #ccc; } .file-box{ position:relative;width:640px} .uploadbtn{ height:30px; width:60px; margin-left:3px; float:left; } .but { width:102px; height:25px; line-height:24px; margin-bottom: 3px; border:0px; background:#1381c9; color: #fff; cursor:pointer; } .file{ position:absolute; top:0px; left:0px; height:28px; filter:alpha(opacity:0);opacity: 0;width:310px; line-height:28px; } .tabFirstTr{background:#ededed;font-weight: bold;} </style>
3、用到的JS:
<script type="text/javascript"> //点击“浏览”按钮时出发该事件 function ReadExcel(){ if(!+"\v1"){//IE不支持垂直制表符 ReadExcelForIE();//调用IE的方法 return ; }else{//说明是其他系列,那么调用JXL读取Excel并显示到页面 ReadExcelForOthers(); return; } } //IE系列:使用IE的ActiveXObject读取Excel数据并显示到页面上 function ReadExcelForIE() { //IE8出于安全性的考虑,上传文件时屏蔽了真实的本地文件路径,而以“C:\fakepath\”取代之。 //用下面的方法获取文件的路径(只对IE有效) var file_upl = document.getElementById("upfile"); file_upl.select(); var filePath = document.selection.createRange().text; document.getElementById("textfield").value=filePath;//文本框显示文件路径 if(isempty(filePath)){return ;}//若为空,不处理 //判断当前上传的文件是不是Excel文件 var isDecimal=/^.*\.(?:xls|xlsx)$/; if(!(isDecimal.test(file_upl.value))){ alert("只支持xlsx、xls类型的Excel文件"); document.getElementById("textfield").value="";//文本框值置空 return false; } try { var oXL = new ActiveXObject("Excel.Application"); //创建操作EXCEL应用程序的实例 }catch(e) { alert( "要将该表导出到Excel,您必须安装Excel电子表格软件,同时浏览器须使用“ActiveX 控件”,您的浏览器须允许执行控件。"); return ""; } var oWB = oXL.Workbooks.open(filePath);//打开指定路径的excel文件 var count = oWB.worksheets.Count;//sheet的张数 var str=""; for(var c=1;c<=count;c++){//循环每一张sheet var name =oWB.worksheets(c).name;//每个sheet的名称 oWB.worksheets(c).select();//选定要操作的sheet var oSheet = oWB.ActiveSheet; var rows = oSheet.usedrange.rows.count; //行数 var columns =oSheet.usedrange.columns.count ;//列数 str+="<center><h3>"+name+"</h3></center>"; str+="<table border='0' cellspacing='0' cellpadding='0' class='p_show' width='100%'>"; for(var i=1;i<=rows;i++){ if(i==1){//第一行是th,标题 str+="<tr class='tabFirstTr'>"; for(var j=1;j<=columns;j++){ //Excel中每一个sheet默认有36列,但并不是每一列都有数据,获取第一行的数据,判断当没有数据时就不再循环并记录columns,下面的数据只需要循环columns列而不再是36列 if(isempty(oSheet.cells(i,j).value)){ columns=j-1; break; }else{ str+="<td>"+oSheet.cells(i,j).value+"</td>"; } } str+="</tr>"; }else{ str +="<tr>"; for(var k=1;k<=columns;k++){ str+="<td>"+setValue(oSheet.cells(i,k).value)+"</td>"; } str +="</tr>"; } } str+="</table>"; } document.getElementById("show").innerHTML=str; //oWB.close(); //退出操作excel的实例对象 //oXL.quit(); oXL.Visible = true; //设置excel为可见 //很重要,不能省略,不然会出问题 意思是excel交由用户控制 oXL.UserControl = true; oSheet = null; oWB = null; oXL = null; //手动调用垃圾收集器 CollectGarbage(); } //IE以外的其他浏览器:调用JXL读取Excel并显示到页面 function ReadExcelForOthers(){ //1、获取选定的Excel文件路径 var filePath = document.getElementById("upfile"); if(!isempty(filePath.value)){ //2、验证文件是不是Excel文件 var isDecimal=/^.*\.(?:xls)$/; if(!(isDecimal.test(filePath.value))){ alert("只支持xlsx类型的Excel文件"); document.getElementById("textfield").value="";//文本框值置空 return false; } //3、通过AJAX上传文件并将返回的结果显示到页面 var option={ async:false, type:"POST", url:"<%=path%>/ReadExcelServlet", success:function(data){ if(data!="false"){ document.getElementById("show").innerHTML=data; }else{ alert("上传失败!"); } } }; $("#fileForm").ajaxSubmit(option); }else{ alert("请选择文件!"); } } //设置值,若为空,则用 代替 function setValue(_value){ if((_value+" ").indexOf("UTC")!=-1){ var vDate = new Date(_value); _value=(vDate.getFullYear())+"-"+(vDate.getMonth()+1)+"-"+(vDate.getDate()); return _value; }else{ return isempty(_value)?" ":_value; } } /** *验证js变量的值是否为空, * true-不存在 * false-存在 * */ function isempty(v){ switch (typeof v){ case 'undefined' : return true; case 'string' : if((v.replace(/(^\s*)|(\s*$)/, "")).length == 0) return true; break; case 'boolean' : if(!v) return true; break; case 'number' : if(0 === v) return true; break; case 'object' : if(null === v) return true; if(undefined !== v.length && v.length==0) return true; for(var k in v){return false;} return true; break; } return false; } /* var myDate = new Date(); myDate.getYear(); //获取当前年份(2位) myDate.getFullYear(); //获取完整的年份(4位,1970-????) myDate.getMonth(); //获取当前月份(0-11,0代表1月) myDate.getDate(); //获取当前日(1-31) myDate.getDay(); //获取当前星期X(0-6,0代表星期天) myDate.getTime(); //获取当前时间(从1970.1.1开始的毫秒数) myDate.getHours(); //获取当前小时数(0-23) myDate.getMinutes(); //获取当前分钟数(0-59) myDate.getSeconds(); //获取当前秒数(0-59) myDate.getMilliseconds(); //获取当前毫秒数(0-999) myDate.toLocaleDateString(); //获取当前日期 var mytime=myDate.toLocaleTimeString(); //获取当前时间 myDate.toLocaleString( ); //获取日期与时间 */ </script>
4、用到的Servlet:
import java.io.File; import java.io.IOException; import java.io.PrintWriter; import java.util.Date; import java.util.Iterator; import java.util.List; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import jxl.Sheet; import jxl.Workbook; import org.apache.commons.fileupload.FileItem; import org.apache.commons.fileupload.disk.DiskFileItemFactory; import org.apache.commons.fileupload.servlet.ServletFileUpload; public class ReadExcelServlet extends HttpServlet { File tmpDir = null;//初始化上传文件的临时存放目录 File saveDir = null;//初始化上传文件后的保存目录 public ReadExcelServlet() { super(); } public void destroy() { super.destroy(); // Just puts "destroy" string in log // Put your code here } public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html"); this.doPost(request, response); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html"); request.setCharacterEncoding("UTF-8"); response.setCharacterEncoding("UTF-8"); String retuStr=""; //1、上传文件 //判断是普通表单,还是带文件上传的表单。 //文件上传的表单有enctype="multipart/form-data"的属性,取值时不能按普通表单接收值那样直接获取。 if(ServletFileUpload.isMultipartContent(request)) { //用 DiskFileItemFactory 创建新的 file items (只是临时的) //DiskFileItemFactory 创建FileItem 实例,并保存其内容在内存或者硬盘中 //通过一个阀值来决定这个 FileItem 实例是存放在内存或者硬盘中 DiskFileItemFactory factory = new DiskFileItemFactory(); //设置阀值大小,不设置的话,默认10k,超过这个阀值,FileItem将直接写入到磁盘 factory.setSizeThreshold(1024*10); //设置临时文件夹 //不设置,默认为系统默认Temp文件路径,调用 System.getProperty("java.io.tmpDir") 获取 //超过阀值的 FileItem 实例将存放在这个目录中 factory.setRepository(tmpDir); //构造servletFileUpload 的实例,该实例提供工厂模式创建FileItem的DiskFileItemFactory实例 ServletFileUpload fileUpload = new ServletFileUpload(factory); //设置一个完整的请求允许的最大大小(注意是完整请求,包括非file类型的表单,比如Text类型) fileUpload.setSizeMax(10*1024*1024); //设置所允许的最大的单个上传文件大小(对应一个FileItem对象) fileUpload.setFileSizeMax(10*1024*1024); try { //每一个FileItem 对应一个 request 请求中from表单中有name属性的 input 元素 //解析 request 请求,将request中提交的值存入List数组 System.out.println(request.getParameter("textfield")); System.out.println(request.getParameter("uploadFile")); List<FileItem> fileItems = fileUpload.parseRequest(request); Iterator i = fileItems.iterator(); // 依次处理每一个文件: String fileName="";//文件名称 String phyFileName="";//文件物理名称 String filetype="";//文件类型 long filesize=0;//文件大小 String filepath = "";//文件所在物理目录 while (i.hasNext()) { FileItem fi = (FileItem) i.next(); //isFormField方法用来判断FileItem对象里面封装的数据是一个普通文本表单字段,还是一个文件表单字段。 //如果是普通文本表单字段,返回一个true否则返回一个false。因此可以用该方法判断是否是普通表单域还是文件上传表单域。 if (!fi.isFormField()) { //getName方法用来获得文件上传字段中的文件名 //getFieldName方法用来返回表单标签的name属性的值 fileName= fi.getName().substring(fi.getName().lastIndexOf("\\") + 1); //获取文件名称 //此方法用来获得上传文件的类型,即标段字段元素描述头属性“content-type”的值,如image/jpeg。 //如果FileItem对象对应的是普通的表单字段,将返回null。 filetype = fi.getContentType(); //getSize:返回上传文件的大小。 filesize=fi.getSize(); System.out.println("文件类型:"+filetype); System.out.println("大小:"+fi.getSize());//byte //自定义存在数据库中的名称 phyFileName= System.currentTimeMillis()+ fileName.substring(fileName.lastIndexOf(".")); filepath=saveDir +"\\"+ phyFileName; //write方法将FileItem对象中的内容保存到某个指定的文件中。如果FileItem对象中的内容是保存在某个临时文件中, //该方法完成后,临时文件可以会被删除。该方法也可以将普通表单字段保存在一个文件中, //但最主要的用途是把上传的文件内容保存在本地文件系统中。 File excelFile = new File(filepath); fi.write(excelFile); //2、读取上传的Excel文件,拼接HTML显示到页面 Workbook book = Workbook.getWorkbook(excelFile);//获取Excel文件对象,便于读取 //若是以后需要修改,可以使用下面的方法得到正在读取文件的创建对象,便于以后修改 //WritableWorkbook workbook= Workbook.createWorkbook(excelFile, book); //获取是在使用的API的版本(练习用) String version = book.getVersion(); System.out.println("当期使用的API的版本:"+version); //获取当前Excel的sheet数量(练习用) Integer sheetNumber = book.getNumberOfSheets(); System.out.println("当期Excel中的sheet数:"+sheetNumber); //获取每个sheet并读取其中数据 Sheet[] sheets = book.getSheets();//获取当前Excel下所有sheet集合 for(int j=0;j<sheets.length;j++){ Sheet sheet = sheets[j];//获取单个sheet String sheetName = sheet.getName();//当前sheet的名称 System.out.println("第"+(j+1)+"个sheet的名称:"+sheetName); Integer column = sheet.getColumns();//当前sheet所有列 System.out.println("第"+(j+1)+"个sheet的列数:"+column); Integer row = sheet.getRows();//当前sheet所有行 System.out.println("第"+(j+1)+"个sheet的行数:"+row); retuStr +="<center><h3>"+sheetName+"</h3></center>"; retuStr +="<table border='0' cellspacing='0' cellpadding='0' class='p_show' width='100%'>"; for(int m=0;m<row;m++){//一行一行的遍历 if(m==0){//第一行:标题行 retuStr+="<tr class='tabFirstTr'>"; for(int n=0;n<column;n++){ //getCell(int columu,int row):获取第columu列,第row行的单元格,getContents()获取单元格的内容 if(null!=sheet.getCell(n,0).getContents() && !sheet.getCell(n,0).getContents().equals("")){//不为空,进行拼接 retuStr+="<td>"+sheet.getCell(n,0).getContents()+"</td>"; }else{ column=n-1;//避免空的列也显示出来 break; } } retuStr+="</tr>"; }else{//其他内容行 retuStr +="<tr>"; for(int n=0;n<=column;n++){ retuStr+="<td>"+sheet.getCell(n,m).getContents()+"</td>"; } retuStr +="</tr>"; } } retuStr+="</table>"; } } else { //getString方法将FileItem对象中保存的数据流内容以一个字符串返回。 //它有两个重载形式。public java.lang.String getString()和public java.lang.String getString(java.lang.String encodeing) throws java.io.UnsupportedEncodingException。 //前者使用缺省的字符集编码将主体内容转换成字符串, //后者使用参数指定的字符集编码。如果在读取普通表单字段元素的内容时,出现了乱码现象,可以调用第二个方法,并传入 正确的字符集编码名称。 String desc = fi.getString("UTF-8"); System.out.println("文件描述:" + desc); } } }catch (Exception e) { e.printStackTrace(); retuStr="false"; }finally{ } PrintWriter out = response.getWriter(); System.out.println(retuStr); //retuStr = new String(retuStr.getBytes("ISO8859-1"),"UTF-8"); System.out.println(retuStr); out.println(retuStr); out.flush(); out.close(); }else { throw new RuntimeException("请设置form表单的enctype属性"); } } /* 对上传文件夹和临时文件夹进行初始化 */ @Override public void init() throws ServletException { super.init(); String tmpPath = "D:\\tmpDir"; String savePath = "D:\\saveDir"; tmpDir = new File(tmpPath);//通过将给定路径名字符串转换为抽象路径名来创建一个新 File实例。 saveDir = new File(savePath); if(!tmpDir.isDirectory())//测试此抽象路径名表示的文件是否是一个目录 tmpDir.mkdir(); //若不是,则创建此抽象路径名指定的目录 if(!saveDir.isDirectory()) saveDir.mkdir(); } }
5、用到的JS JAR包:
<script type="text/javascript" src="js/jquery-1.4.2.js"></script> <script type="text/javascript" src="js/jquery.form.js"></script>
其中,jquery-1.4.2.js是Jquery的核心jar包,jquery.form.js主要是用来上传文件的。JS中有一段使用AJAX上传文件的代码用的就是这个,它支持多个form表单同时上传文件(一个页面有多个form表单,每个form表单上传一个文件,点“提交”时一并上传)。
其他的JavaJAR包已经以附件的形式上传,有需要的可以下载。
6、期间遇到的两个问题:
a、报错:Unable to recognize OLE stream。JXL只支持2003的Excel,要是给它个2007的它就报这错了。所以在选择文件时需要判定一下。
b、List<FileItem> fileItems = fileUpload.parseRequest(request);这个fileItems集合的size一直都只有1。在网上查的资料大部分都是说因为form表没有设置enctype="multipart/form-data"属性导致fileItems为空的情况,我这个不为空但是size不对,应该是3而不是1,因为“每一个FileItem 对应一个 request 请求中form表单中的 input 元素",我这有三个input应该就有三个FileItem。后来查了一下,发现我的三个input里面只有一个有name属性,其他两个都木有。于是再次理解了一回form表单传值的机制(传值时只认name不认ID的)。上面那句话也应该改成:”每一个FileItem 对应一个 request 请求中form表单中有name属性的 input 元素“。
7、抱怨几句:
iteye对代码的支持还是挺给力的,就是感觉可选的类型太少,最基本的CSS样式好像都不支持(一粘上午全是黑的,不像CSS原来的代码那样红红绿绿的),是真的不支持还是我没找到?有知道给我留个言我也好学一招。谢谢!