读取Excel中的数据显示到页面(IE,火狐)

前面有写一篇叫做“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("请选择文件!");
	}
 }
  
//设置值,若为空,则用&nbsp;代替
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)?"&nbsp;":_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原来的代码那样红红绿绿的),是真的不支持还是我没找到?有知道给我留个言我也好学一招。谢谢!

 

你可能感兴趣的:(JXL,读取Excel文件)