成都心情

  BlogJava :: 首页 ::  :: 联系 :: 聚合  :: 管理 ::
  98 随笔 :: 2 文章 :: 501 评论 :: 1 Trackbacks

最近遇到点读取 Excel 数据的问题,于是花了点时间找开源工具。
要解析 Excel,首当其冲的是上传文件,以前在项目里我们用 SmartUpload 进行上传,不过这个项目似乎已经停止开发了,于是在这里我使用 Apache Commons FileUpload,可以在 http://jakarta.apache.org/commons/fileupload 找到。目前该项目的最新版本是 1.1.1,网上有大量的范例程序,不过后来用的时候发现大部分方法在新版本中都不推荐使用了,于是好好读了一回 API 和官方范例。

先来看看如何上传文件,Servlet 很简单,在这里我限制了最大上传量为 1M,且直接读进内存中,不进行磁盘临时文件缓存。

import  java.io.IOException;
import  java.io.PrintWriter;
import  java.io.File;
import  java.net.URI;
import  java.net.URL;

import  javax.servlet.ServletException;
import  javax.servlet.http.HttpServlet;
import  javax.servlet.http.HttpServletRequest;
import  javax.servlet.http.HttpServletResponse;

import  java.util.List;

import  org.apache.commons.fileupload.RequestContext;
import  org.apache.commons.fileupload.servlet.ServletRequestContext;
import  org.apache.commons.fileupload.servlet.ServletFileUpload;
import  org.apache.commons.fileupload.disk.DiskFileItemFactory;
import  org.apache.commons.fileupload.FileItem;

public   class  UploadServlet  extends  HttpServlet {

    
/**
     * Constructor of the object.
     
*/
    
public  UploadServlet() {
        
super ();
    }

    
/**
     * Destruction of the servlet.
     
*/
    
public   void  destroy() {
        
super .destroy();
    }

    
public   void  doGet(HttpServletRequest request, HttpServletResponse response)
            
throws  ServletException, IOException {
    }

    
/**
     * 上传文件
     * 
     * 
@param  request
     * 
@param  response
     * 
@throws  ServletException
     * 
@throws  IOException
     
*/
    
public   void  doPost(HttpServletRequest request, HttpServletResponse response)
            
throws  ServletException, IOException {
        response.setContentType(
" text/html " );
        response.setCharacterEncoding(
" gbk " );
        PrintWriter out 
=  response.getWriter();
        out.println(
" <html> " );
        out.println(
"   <head><title>提示</title></head> " );
        out.println(
"   <body> " );
        
//  不用获取 URL 对象也行,直接用 getServletContext().getRealPath("/") 代替。
        URL url  =  getServletContext().getResource( " / " );
        
//  从 HTTP servlet 获取 fileupload 组件需要的内容
        RequestContext requestContext  =   new  ServletRequestContext(request);
        
//  判断是否包含 multipart 内容
         if  (ServletFileUpload.isMultipartContent(requestContext)) {
            
//  创建基于磁盘的文件工厂
            DiskFileItemFactory factory  =   new  DiskFileItemFactory();
            
//  设置直接存储文件的极限大小,一旦超过则写入临时文件以节约内存。默认为 1024 字节
            factory.setSizeThreshold( 1024   *   1024 );
            
//  创建上传处理器,可以处理从单个 HTML 上传的多个上传文件。
            ServletFileUpload upload  =   new  ServletFileUpload(factory);
            
//  最大允许上传的文件大小
            upload.setSizeMax( 1024   *   1024 );
            
//  处理上传
            List items  =   null ;
            
try  {
                items 
=  upload.parseRequest(requestContext);
                
//  由于提交了表单字段信息,需要进行循环区分。
                 for  ( int  i  =   0 ; i  <  items.size(); i ++ ) {
                    FileItem fi 
=  (FileItem) items.get(i);
                    
//  如果不是表单内容,取出 multipart。
                     if  ( ! fi.isFormField()) {
                        
//  上传文件路径和文件、扩展名。
                        String sourcePath  =  fi.getName();
                        String[] sourcePaths 
=  sourcePath.split( " \\\\ " );
                        
//  获取真实文件名
                        String fileName  =  sourcePaths[sourcePaths.length  -   1 ];
                        
//  创建一个待写文件
                        File uploadedFile  =   new  File( new  URI(url.toString() + fileName));
                        
//  写入
                        fi.write(uploadedFile);
                        out.println(fileName
+ " 上传成功。 " );
                    }
                }
            } 
catch  (Exception e) {
                out.println(
" 上传失败,请检查上传文件大小是否超过1兆,并保证在上传时该文件没有被其他程序占用。 " );
                out.println(
" <br>原因: " + e.toString());
                e.printStackTrace();
            }
        }
        out.println(
"   </body> " );
        out.println(
" </html> " );
        out.flush();
        out.close();
    }

    
/**
     * Initialization of the servlet.
     * 
     * 
@throws  ServletException
     
*/
    
public   void  init()  throws  ServletException {
    }
}


上面的程序示范了如何上传文件到服务器,本文的主要目的不光是上传,还要进行 Excel 解析,抽取有用的内容。开源的 Excel 解析器很多,在此我选择了 JExcelApi,可以在 http://jexcelapi.sourceforge.net 找到,据说是韩国人开发的,最新版本是 2.6.2。为什么没有选 POI,原因也是因为它 N 久没有更新了。我总是喜欢最新的东东,比如 Adobe 的 PDF Reader,硬是下载了 8.0,结果感觉还没有 6.0 好用。:(

以下程序修改直上传,做了部分调整,取消了文件储存,直接通过读取输入流进行解析,并假设约定的 Excel 文件有五列 N 行,第一行为标题信息。

import  java.io.IOException;
import  java.io.PrintWriter;

import  javax.servlet.ServletException;
import  javax.servlet.http.HttpServlet;
import  javax.servlet.http.HttpServletRequest;
import  javax.servlet.http.HttpServletResponse;

import  java.util.List;

import  org.apache.commons.fileupload.RequestContext;
import  org.apache.commons.fileupload.servlet.ServletRequestContext;
import  org.apache.commons.fileupload.servlet.ServletFileUpload;
import  org.apache.commons.fileupload.disk.DiskFileItemFactory;
import  org.apache.commons.fileupload.FileItem;

import  jxl.Workbook;
import  jxl.Sheet;
import  jxl.Cell;

public   class  UploadServlet  extends  HttpServlet {
        
    
/**
     * Constructor of the object.
     
*/
    
public  UploadServlet() {
        
super ();
    }

    
/**
     * Destruction of the servlet.
     
*/
    
public   void  destroy() {
        
super .destroy();
    }

    
public   void  doGet(HttpServletRequest request, HttpServletResponse response)
            
throws  ServletException, IOException {
    }

    
/**
     * 上传文件
     * 
     * 
@param  request
     * 
@param  response
     * 
@throws  ServletException
     * 
@throws  IOException
     
*/
    
public   void  doPost(HttpServletRequest request, HttpServletResponse response)
            
throws  ServletException, IOException {
        response.setContentType(
" text/html " );
        response.setCharacterEncoding(
" gbk " );
        PrintWriter out 
=  response.getWriter();
        out.println(
" <html> " );
        out.println(
"   <head><title>提示</title></head> " );
        out.println(
"   <body> " );
        
//  声明文件域
        FileItem fileItem  =   null ;
        
//  从 HTTP servlet 获取 fileupload 组件需要的内容
        RequestContext requestContext  =   new  ServletRequestContext(request);
        
//  判断是否包含 multipart 内容,如果不包含,则不进行任何处理。
         if  (ServletFileUpload.isMultipartContent(requestContext)) {
            
//  创建基于磁盘的文件工厂
            DiskFileItemFactory factory  =   new  DiskFileItemFactory();
            
//  设置直接存储文件的极限大小,一旦超过则写入临时文件以节约内存。默认为 1024 字节
            factory.setSizeThreshold( 1024   *   1024 );
            
//  创建上传处理器,可以处理从单个 HTML 上传的多个上传文件。
            ServletFileUpload upload  =   new  ServletFileUpload(factory);
            
//  最大允许上传的文件大小
            upload.setSizeMax( 1024   *   1024 );
            
try  {
                
//  处理上传
                List items  =   null ;
                items 
=  upload.parseRequest(requestContext);
                
//  由于提交了表单字段信息,需要进行循环区分。
                 for  ( int  i  =   0 ; i  <  items.size(); i ++ ) {
                    FileItem fi 
=  (FileItem) items.get(i);
                    
//  如果不是表单内容,取出 multipart。
                     if  ( ! fi.isFormField()) {
                        fileItem 
=  fi;
                        
// 一次只上传单个文件
                         break ;
                    }
                }
                out.println(parseExcel(fileItem));
            } 
catch  (Exception e) {
                out.println(
" 上传失败!请检查上传的文件是否为excel格式、信息是否完整完整、且大小是否超过1兆。 " );
                out.println(
" <br>原因: " + e.toString());
                e.printStackTrace();
            }
        }
        out.println(
"   </body> " );
        out.println(
" </html> " );
        out.flush();
        out.close();
    }

    
/**
     * 分析excel文件
     * 
     * 
@param  FileItem fi 文件域
     * 
@return  String
     * 
@throws  Exception
     
*/
    
private  String parseExcel(FileItem fi)  throws  Exception{
        
//  声明 Workbook
        Workbook workbook  =   null ;
        
try {
            workbook 
=  Workbook.getWorkbook(fi.getInputStream());
            Sheet sheet 
=  workbook.getSheet( 0 );
            
// 总行数
             int  count  =  sheet.getRows();
            
// 取出标题
              String a1  =  sheet.getCell( 0 , 0 ).getContents();
              String a2 
=  sheet.getCell( 1 , 0 ).getContents();
              String a3 
=  sheet.getCell( 2 , 0 ).getContents();
              String a4 
=  sheet.getCell( 3 , 0 ).getContents();
              String a5 
=  sheet.getCell( 4 , 0 ).getContents();
            
// 取出内容
             for ( int  i  =   1 ;i  <  count;i ++ ){
                Cell[] cells 
=  sheet.getRow(i);
                System.out.println(cells[
0 ].getContents()
                        
+ cells[ 1 ].getContents() + cells[ 2 ].getContents()
                        
+ cells[ 3 ].getContents() + cells[ 4 ].getContents());
            }
            
return   " 上传成功。 " ;            
        }
catch (Exception e){
            
throw  e;
        }
finally {
            
if (workbook != null ){
                workbook.close();
            }
        }
    }
    
    
/**
     * Initialization of the servlet.
     * 
     * 
@throws  ServletException
     
*/
    
public   void  init()  throws  ServletException {
    }
}

JExcelApi 用起来很简单,而且还可以根据 Excel 中数据类型转换成 Java 数据类型,比如 int、double,具体信息可以参考它的开发指南。当然,本范例还提供现构造 Excel 然后下载的方法,如果以后遇到,一定继续完善。

------------------------------------------------------------------------------------------------
关于生成 excel 和下载,一月份的文章还留了个尾巴,今天把它补充上去。2007-04-22 by rosen jiang

代码如下,放在 servlet 中,io 异常我没捕获,直接由 get or post 方法抛出,当然,如果更严谨点可以放在 finally 里关闭。
        //设置输出格式和头信息
        response.setContentType("application/x-msdownload;charset=GBK");
        String filename 
= new String("供应商报价清单.xls".getBytes("GBK"),"ISO_8859_1");
        response.setHeader(
"Content-Disposition","attachment;filename="+filename);

        
//虚拟数据
        String materialName = "马桶";       //材料名
        String size = "200×300";           //规格
        String unit = "";                //单位
        String qty = "2";                  //数量
        String band = "不知道牌子";          //材料品牌
        String company = "成都某厂";         //厂家名
        String memo = "质量可靠";            //备注
        String price = "20.30";            //价格
        String repDate = "2007-04-11";     //报价时间
        List<String[]> list = new ArrayList<String[]>();
        
for(int i = 10; i > 0; i--){
            String[] outPut 
= {materialName,size,unit,qty+i,band,company,memo,price,repDate};
            list.add(outPut);
        }

        
//输出流
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        
//构造工作区
        WritableWorkbook workbook = Workbook.createWorkbook(baos);
        
//构造 sheet
        WritableSheet sheet = workbook.createSheet("报价清单"0);
        
//构造粗标题字体
        WritableFont blodFont = new WritableFont(WritableFont.TAHOMA,10,WritableFont.BOLD, false);
        WritableCellFormat blodFormat 
= new WritableCellFormat (blodFont);
        Label label 
= null;
        
try {
            
//标题行
            label = new Label(00"材料名", blodFormat);
            sheet.addCell(label);
            label 
= new Label(10"规格", blodFormat);
            sheet.addCell(label);
            label 
= new Label(20"单位", blodFormat);
            sheet.addCell(label);
            label 
= new Label(30"数量", blodFormat);
            sheet.addCell(label);
            label 
= new Label(40"材料品牌", blodFormat);
            sheet.addCell(label);
            label 
= new Label(50"厂家名", blodFormat);
            sheet.addCell(label);
            label 
= new Label(60"备注", blodFormat);
            sheet.addCell(label);
            label 
= new Label(70"价格", blodFormat);
            sheet.addCell(label);
            label 
= new Label(80"报价时间", blodFormat);
            sheet.addCell(label);
            
//输出业务数据
            for(int i = 1; i <= list.size(); i++){
                String[] outPut 
= list.get(i-1);
                label 
= new Label(0, i, outPut[0]);
                sheet.addCell(label);
                label 
= new Label(1, i, outPut[1]);
                sheet.addCell(label);
                label 
= new Label(2, i, outPut[2]);
                sheet.addCell(label);
                label 
= new Label(3, i, outPut[3]);
                sheet.addCell(label);
                label 
= new Label(4, i, outPut[4]);
                sheet.addCell(label);
                label 
= new Label(5, i, outPut[5]);
                sheet.addCell(label);
                label 
= new Label(6, i, outPut[6]);
                sheet.addCell(label);
                label 
= new Label(7, i, outPut[7]);
                sheet.addCell(label);
                label 
= new Label(8, i, repDate);
                sheet.addCell(label);
            }
            
//写入文件
            workbook.write();
            workbook.close();

            
//向浏览器返回文件流
            OutputStream os = response.getOutputStream();
            os.write(baos.toByteArray());
            os.flush();
            os.close();
            baos.close();
        } 
catch (RowsExceededException e) {
            e.printStackTrace();
        } 
catch (WriteException e) {
            e.printStackTrace();
        }
    }

生成 excel 的样子是这样的:
writeexcel.jpg

请注意!引用、转贴本文应注明原作者:Rosen Jiang 以及出处:http://www.blogjava.net/rosen
posted on 2007-01-19 15:19 Rosen 阅读(8142) 评论(14)  编辑  收藏 所属分类: Java EE 服务器端

评论

# re: Apache FileUpload 上传以及 JExcelApi 解析 2007-01-20 15:54 nesta
不知道读取date类型有没有遇到过问题呢??  回复  更多评论
  

# re: Apache FileUpload 上传以及 JExcelApi 解析 2007-01-20 17:56 Rosen
@nesta
hi nesta,问题倒是没遇到,不过有点奇怪的是访问构造函数 SimpleDateFormat("yyyy-mm-dd"),格式化出来的日期中间少了月份。
下面是代码,我假设 excel 中第四个字段是日期格式("2007-9-1"),然后取出。
if(cells[3].getType()==CellType.DATE){
DateCell dc = (DateCell)cells[3];
Date d = dc.getDate();
DateFormat df = SimpleDateFormat.getDateInstance();
System.out.println(df.format(d));
}
  回复  更多评论
  

# re: Apache FileUpload 上传以及 JExcelApi 解析 2007-01-20 21:22 Anubis
yyyy-MM-dd  回复  更多评论
  

# re: Apache FileUpload 上传以及 JExcelApi 解析 2007-01-20 21:30 Rosen
@Anubis
呵呵,谢谢提醒,和数据库的格式弄混淆了。  回复  更多评论
  

# re: Apache FileUpload 上传以及 JExcelApi 解析 2007-01-23 23:35 小车马
不错,
还是那句话,开源的没有用不得的,呵呵
楼主,潜力贴论坛(http://content.uu1001.com)是我个人的一个设想,如果你对java非常的专注,并且愿意交我这个朋友,可以发邮件给我(lbw070105@gmail.com),希望我们可以一起发展它。
  回复  更多评论
  

# re: Apache FileUpload 上传以及 JExcelApi 解析 2007-04-03 17:21 sandy
写得好多,好辛苦哦。:)  回复  更多评论
  

# re: Apache FileUpload 上传以及 JExcelApi 解析 2007-04-18 18:55 ryan
That's Great! I love you so much!  回复  更多评论
  

# re: Apache FileUpload 上传以及 JExcelApi 解析 2007-05-21 15:32 windows
楼主你好,问个问题.
/**
* 分析excel文件
*

// 取出标题
String a1 = sheet.getCell( 0 , 0 ).getContents();

-----------------------
如果读取的是个空的EXCEL文件,a1的值为空啊!!如何判断为空啊?假设判断:(0,0)

希望楼主赐教.
  回复  更多评论
  

# re: Apache FileUpload 上传以及 JExcelApi 解析 2007-05-21 22:32 Rosen
@windows
我还没测试过为空的情况,在文章中,我是充分信任传入的excel是完整有效的。就像你说的,我想如果0,0为空,那么a1应该equals(""),由于现在手头没有测试环境,你要自己去试试了。  回复  更多评论
  

# re: Apache FileUpload 上传以及 JExcelApi 解析 2007-05-22 09:43 windows
@Rosen
我用:"".equals(sheet.getCell( 0 , 0 ).getContents())
"".eqlals(sheet.getCell( 0 , 0 ))
sheet.getCell( 0 , 0 ).getContents()==null
sheet.getCell( 0 , 0 )==null
都试过了,不管方法合不合理.都是下面的结果...郁闷啊....
java.lang.ArrayIndexOutOfBoundsException: 0
jxl.read.biff.SheetImpl.getCell(SheetImpl.java:354)
  回复  更多评论
  

# re: Apache FileUpload 上传以及 JExcelApi 解析 2007-05-24 10:42 Rosen
@windows
我看见 sheet 有 getColumns() 方法,返回 int,这样就可以先判断有没有实际的列数据。  回复  更多评论
  

# re: Apache FileUpload 上传以及 JExcelApi 解析 2007-06-04 10:25 Fantasysoft
Rosen兄,找不到联系您的方式,只好在这里留个言了。

见到留言后,烦请您和我联系一下好吗? 我的EMail: fantasysoft at gmail.com. 谢谢。  回复  更多评论
  

# re: Apache FileUpload 上传以及 JExcelApi 解析 2007-06-04 20:22 Rosen
@Fantasysoft
已经加你的 msn 了,主要是现在 msn 上面好友太多,所以就没有公布出来。  回复  更多评论
  

# re: Apache FileUpload 上传以及 JExcelApi 解析 2009-06-25 18:26 strawbingo
有问题请教一下,我msn是straw_bingo@hotmail.com。谢谢  回复  更多评论
  


只有注册用户登录后才能发表评论。


网站导航: