最近遇到点读取 Excel 数据的问题,于是花了点时间找开源工具。
要解析 Excel,首当其冲的是上传文件,以前在项目里我们用 SmartUpload 进行上传,不过这个项目似乎已经停止开发了,于是在这里我使用 Apache Commons FileUpload,可以在 http://jakarta.apache.org/commons/fileupload 找到。目前该项目的最新版本是 1.1.1,网上有大量的范例程序,不过后来用的时候发现大部分方法在新版本中都不推荐使用了,于是好好读了一回 API 和官方范例。
先来看看如何上传文件,Servlet 很简单,在这里我限制了最大上传量为 1M,且直接读进内存中,不进行磁盘临时文件缓存。
表单上一定要加ENCTYPE="multipart/form-data"
<form id="frm_reqNew" name="frm_reqNew" ENCTYPE="multipart/form-data" action="$!actionPath/deliver/deliverReqCreate.shtml" method="post" >
-------------------------------------------------------------------------------------------------------------------
上面的程序示范了如何上传文件到服务器,本文的主要目的不光是上传,还要进行 Excel 解析,抽取有用的内容。开源的 Excel 解析器很多,在此我选择了 JExcelApi,可以在 http://jexcelapi.sourceforge.net 找到,据说是韩国人开发的,最新版本是 2.6.2。为什么没有选 POI,原因也是因为它 N 久没有更新了。我总是喜欢最新的东东,比如 Adobe 的 PDF Reader,硬是下载了 8.0,结果感觉还没有 6.0 好用。:(
以下程序修改直上传,做了部分调整,取消了文件储存,直接通过读取输入流进行解析,并假设约定的 Excel 文件有五列 N 行,第一行为标题信息。
JExcelApi 用起来很简单,而且还可以根据 Excel 中数据类型转换成 Java 数据类型,比如 int、double,具体信息可以参考它的开发指南。当然,本范例还提供现构造 Excel 然后下载的方法,如果以后遇到,一定继续完善。
------------------------------------------------------------------------------------------------------------------
生成 excel 和下载
代码如下,放在 servlet 中,io 异常我没捕获,直接由 get or post 方法抛出,当然,如果更严谨点可以放在 finally 里关闭。
要解析 Excel,首当其冲的是上传文件,以前在项目里我们用 SmartUpload 进行上传,不过这个项目似乎已经停止开发了,于是在这里我使用 Apache Commons FileUpload,可以在 http://jakarta.apache.org/commons/fileupload 找到。目前该项目的最新版本是 1.1.1,网上有大量的范例程序,不过后来用的时候发现大部分方法在新版本中都不推荐使用了,于是好好读了一回 API 和官方范例。
先来看看如何上传文件,Servlet 很简单,在这里我限制了最大上传量为 1M,且直接读进内存中,不进行磁盘临时文件缓存。
1 | 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 { } } |
表单上一定要加ENCTYPE="multipart/form-data"
<form id="frm_reqNew" name="frm_reqNew" ENCTYPE="multipart/form-data" action="$!actionPath/deliver/deliverReqCreate.shtml" method="post" >
-------------------------------------------------------------------------------------------------------------------
上面的程序示范了如何上传文件到服务器,本文的主要目的不光是上传,还要进行 Excel 解析,抽取有用的内容。开源的 Excel 解析器很多,在此我选择了 JExcelApi,可以在 http://jexcelapi.sourceforge.net 找到,据说是韩国人开发的,最新版本是 2.6.2。为什么没有选 POI,原因也是因为它 N 久没有更新了。我总是喜欢最新的东东,比如 Adobe 的 PDF Reader,硬是下载了 8.0,结果感觉还没有 6.0 好用。:(
以下程序修改直上传,做了部分调整,取消了文件储存,直接通过读取输入流进行解析,并假设约定的 Excel 文件有五列 N 行,第一行为标题信息。
1 | 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 和下载
代码如下,放在 servlet 中,io 异常我没捕获,直接由 get or post 方法抛出,当然,如果更严谨点可以放在 finally 里关闭。
1 | //设置输出格式和头信息 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(0, 0, "材料名", blodFormat); sheet.addCell(label); label = new Label(1, 0, "规格", blodFormat); sheet.addCell(label); label = new Label(2, 0, "单位", blodFormat); sheet.addCell(label); label = new Label(3, 0, "数量", blodFormat); sheet.addCell(label); label = new Label(4, 0, "材料品牌", blodFormat); sheet.addCell(label); label = new Label(5, 0, "厂家名", blodFormat); sheet.addCell(label); label = new Label(6, 0, "备注", blodFormat); sheet.addCell(label); label = new Label(7, 0, "价格", blodFormat); sheet.addCell(label); label = new Label(8, 0, "报价时间", 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(); } } |