java web Excel导入、导出的实现
2015-11-02 21:54
453 查看
在做web开发时,我们经常会用到数据表格的导入导出功能,这样可以帮我们节省人工操作的时间,极大提高办事效率,下面,直入正题:
笔者所做的导入导出是针对java springMVC框架、工作簿poi以及前端plupload.js插件设计的。
![](http://img.blog.csdn.net/20151102215115692)
导入菜单包含模板下载和导入数据,如下图:
![](http://img.blog.csdn.net/20151102220355192)
点击模板下载,弹出模板下载对话框,如下图:
![](http://img.blog.csdn.net/20151102220649390)
点击导入数据,弹出导入对话框,如下图:
![](http://img.blog.csdn.net/20151102220853138)
接下来,再来看看导出,包含导出当前页和导出全部,如下图:
![](http://img.blog.csdn.net/20151102221019470)
点击导出当前页或是导出全部后,生成excel,如下图:
![](http://img.blog.csdn.net/20151102221228890)
最后再来看看导入模板和生成的excel,如下图:
![](http://img.blog.csdn.net/20151102221431768)
我的模板是放在src/main/webapp/template下的。
然后,再来看看js:
然后,再来看看导出对话框对应的jsp页面import.jsp:
再来看看其对应的import.js:
其中的chunk的含义,笔者稍作解释,如果文件的大小超过chunk_size,那么此文件将分多次请求上传,待会儿就可以看到后台处理上传的请求逻辑。
上面的回调函数是用于导入成功后重新加载datagrid。
以及对应的service实现:
再来看看模板下载以及文件上传的处理类FileHandleCtrl.java:
以上的导入导出,笔者讲得很概括,只是贴出了代码,具体的含义还得大家去领悟,由于不同的框架所使用的导入导出有可能不一致,不过大体的思路应该是一致的,有兴趣的小伙伴可以自己去尝试下,挺有意思的!
笔者所做的导入导出是针对java springMVC框架、工作簿poi以及前端plupload.js插件设计的。
第一步、总体介绍
首先,来看页面展示,如下图:导入菜单包含模板下载和导入数据,如下图:
点击模板下载,弹出模板下载对话框,如下图:
点击导入数据,弹出导入对话框,如下图:
接下来,再来看看导出,包含导出当前页和导出全部,如下图:
点击导出当前页或是导出全部后,生成excel,如下图:
最后再来看看导入模板和生成的excel,如下图:
我的模板是放在src/main/webapp/template下的。
第二步、模板下载和导入
首先,来看看前台导入、导出的jsp代码:<!-- 导出,当前页和页大小隐藏域 --> <input type="hidden" name="currentPage" id="current" value="0"/> <input type="hidden" name="pageSize" id="size" value="0"/> ID:<input type="text" name="id" id="userId"/> 姓名:<input type="text" name="userName"/> <a href="javascript:void(0);" id="search">搜索</a> </form> </div> <!-- 导入 --> <a type="import" id="import" class="easyui-menubutton" suffix="xls" resouseCode="user" importurl="sys/user/importData.do" callback="myCallback">导入</a> <!-- 导出 --> <a type="export" id="export" class="easyui-menubutton">导出</a>
然后,再来看看js:
$(function() { var uploadsubmenu = '<div id="upload_sub" style="width:100px;">'+ '<div id="download_templ"><span class="exportDownload"></span>模板下载</div>'+ '<h1 class="excel-line" id="line1"></h1>'+ '<div id="import_data"><span class="exportData"></span>导入数据</div>'+ '</div>'; if($("#upload_sub").length<1){ $("body").append(uploadsubmenu); } //将导入设为菜单按钮 var importMenu = $('#import').menubutton({ menu: "#upload_sub", iconCls:"icon-import" }); //为子菜单添加点击事件 $(importMenu.menubutton('options').menu).menu({ onClick: function (item) { if(item.id == 'download_templ'){ var resouseCode = $('#import').attr('resouseCode'); var suffix = $('#import').attr('suffix'); loadTemplate(resouseCode, suffix); } if(item.id == 'import_data'){ var importurl = $('#import').attr('importurl'); var callback = $('#import').attr('callback'); var resouseCode = $('#import').attr('resouseCode'); makerUpload(importurl, callback, resouseCode); } } }); }); /** * 模板下载 */ function loadTemplate(resouseCode, suffix) { //文件名+后缀名 var fileName = resouseCode + '.' + suffix var templatediv = '<div id="templatediv" style="width:400px;height:200px;">'+ '</div>'; if (window.top.$('#templatediv').length == 0) { window.top.$("body").append(templatediv); } var templistspan = ""; templistspan +='<div class="download-list">' templistspan += '<span><a id="downloadBtn" href="file/fileHandle/downloadtemplate.do?fileName='+encodeURI(encodeURI( fileName))+'">'+fileName+'</a></span>' templistspan +='</div>'; window.top.$("#templatediv").html(templistspan); //弹出下载模板 window.top.$("#templatediv").dialog({ title:"下载模板", modal:true }); //下载模板完了就关闭对话框 window.top.$("#downloadBtn").click(function() { window.top.$("#templatediv").dialog('close'); }); } /** * 上传导入 */ function makerUpload(importurl, callback, resouseCode) { console.log('弹出导入对话框。。'); //用户详情对话框 var importWindow = window.top.$('#importWin'); if(importWindow.length <= 0) { importWindow = window.top.$("<div id='importWin'/>").appendTo(window.top.document.body); } importWindow.window({ title:'导入', closed: false, closable: true, draggable: true, resizable: true, width: 500, height: 350, modal: true, href: 'sys/redirect/redirectHomePage.do?path=import&callback=' + callback + '&resouseCode=' + resouseCode + '&importurl=' + importurl, onClose: function() { importWindow.window("destroy"); } }); }
然后,再来看看导出对话框对应的jsp页面import.jsp:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <% String callback = request.getParameter("callback"); String importurl = request.getParameter("importurl"); String resouseCode = request.getParameter("resouseCode"); System.out.println("callback:" + callback); System.out.println("importurl:" + importurl); System.out.println("resouseCode:" + resouseCode); %> <link rel="stylesheet" href="js/plugs/plupload/queue/css/jquery.plupload.queue.css" type="text/css"></link> <script type="text/javascript" src="js/plugs/plupload/plupload.js"></script> <script type="text/javascript" src="js/plugs/plupload/plupload.html4.js"></script> <script type="text/javascript" src="js/plugs/plupload/plupload.html5.js"></script> <script type="text/javascript" src="js/plugs/plupload/plupload.flash.js"></script> <script type="text/javascript" src="js/plugs/plupload/zh_CN.js"></script> <script type="text/javascript" src="js/plugs/plupload/queue/jquery.plupload.queue.js"></script> <script type="text/javascript"> var callback = '<%=callback%>'; var resouseCode = '<%=resouseCode%>'; var importurl = '<%=importurl%>'; console.log('callback:', callback); console.log('resouseCode:', resouseCode); console.log('importurl:', importurl); </script> <!-- <div style="width:100%;height:100%;position:relative;"> <button id="startImport" style="position:relative;top:224px;left:50px;height:20px;">开始导入</button> </div> --> <form id="form"> <div id="uploader"> </div> </form> <script type="text/javascript" src="resources/logicJS/common/common.js"></script> <script type="text/javascript" src="resources/logicJS/common/import.js"></script>
再来看看其对应的import.js:
/*$(function() { console.log('windows是否一致', window === window.top); $('#startImport').click(function() { console.log('开始导入'); var iframe = $('#mainPanel > iframe' ,window.top.document)[0]; var currentChildWindow = iframe.contentWindow || iframe.window; console.log('callback', callback); currentChildWindow[callback]('qiyongkang'); }); });*/ $(function() { var files = []; var errors = []; var type = 'file'; var chunk = true; var startbutton = null; var des = "<div class='upload-sm'>"; des += "单个文件最大不超过1mb; "; des += "批量上传文件个数不超过1个; "; des += "上传文件格式为:xls"; des += "</div>"; $("#uploader").pluploadQueue($.extend({ runtimes : 'flash,html4,html5', url : 'file/fileHandle/upload.do?resouseCode=' + resouseCode, max_file_size : '1mb', file_data_name:'file', filters : [], dragdrop : false, flash_swf_url : 'js/plugs/plupload/plupload.flash.swf', init:{ Init:function(uploader){ //开始上传按钮 startbutton = $(".plupload_start"); $(".plupload_header").html(des); }, QueueChanged:function(uploader){ if(uploader.files.length > 1){ var error = "<div class='upload-sm'>"; error += "<font color=red>最多只能上传1个</font>"; error += "</div>"; $(".plupload_header").html(error); startbutton.hide(); }else{ $(".plupload_header").html(des); startbutton.show(); } }, FileUploaded:function(uploader,file,response){ if(response.response){ var rs = $.parseJSON(response.response); console.log(rs); if(rs.status){ files.push(file.name); }else{ errors.push(file.name); } } }, UploadProgress:function(uploader,fs){ var begin = "<div class='upload-sm'>"; begin +="开始上传文件<img src='js/plugs/plupload/queue/img/uploading.gif'>"; begin +="</div>"; $(".plupload_header").html(begin); }, //上传完毕后触发 UploadComplete:function(uploader,fs){ var e= errors.length ? ",失败"+errors.length+"个("+errors.join("、")+")。" : "。"; var begin = "<div class='upload-sm'>"; var value=parseInt(fs.length)-parseInt(errors.length); begin +="上传完成!共"+fs.length+"个。成功"+value+"个"; begin +="</div>"; $(".plupload_header").html(begin); importdata(importurl,callback); } } },(chunk ? {chunk_size:'1mb'} : {}))); }); /** * 导入数据 * @param importurl * @param callback */ function importdata(importurl,callback){ var des = "<div class='upload-sm'>"; des +="正在解析入库<img src='js/plugs/plupload/queue/img/uploading.gif'>"; des +="</div>"; $(".plupload_header").html(des); $.ajax({ url:importurl, type:"POST", dataType: "json", success: function (data) { var msg = "<div class='upload-sm'>"; msg +=data.msg; msg +="</div>"; $(".plupload_header").html(msg); //回调,重新加载数据 var iframe = $('#mainPanel > iframe' ,window.top.document)[0]; var currentChildWindow = iframe.contentWindow || iframe.window; console.log('callback', callback); currentChildWindow[callback]('qiyongkang'); } }); }
其中的chunk的含义,笔者稍作解释,如果文件的大小超过chunk_size,那么此文件将分多次请求上传,待会儿就可以看到后台处理上传的请求逻辑。
第三步、导出当前页和导出全部
对应的jsp代码已在第二步给出,再来看导出对应的js:/** * 初始化导出按钮 */ function initMyExportMenu(){ var exportsubmenu = '<div id="exportMenu_sub">'+ '<div id="export_crrent"><span class="exportcurrent"></span>导出当前页</div>'+ '<h1 class="excel-line"></h1>'+ '<div id="export_all"><span class="exportall"></span>导出全部</div>'+ '</div>'; $("body").append(exportsubmenu); var exports = $("a[type='export']"); $.each(exports,function(data){ var exportMenu = $(this).menubutton({ menu: "#exportMenu_sub", iconCls:"icon-export" }); $(exportMenu.menubutton('options').menu).menu({ onHide:function(){ }, onClick: function (item) { if(item.id=="export_crrent"){ console.log('导出当前页'); var options = $('#userListTab').datagrid('getPager').data("pagination").options; var curr = options.pageNumber; console.log('当前页:', curr); var pageSize = options.pageSize; console.log('页大小', pageSize); //通过隐藏域传参数 $('#current').val(curr); $('#size').val(pageSize); $("#queryForm").form('submit',{ url : 'sys/user/exportData.do', onSubmit : function() { return $(this).form('validate'); }, success : function(data) { } }); } if(item.id=="export_all"){ //通过隐藏域传参数 $('#current').val(0); $('#size').val(0); console.log('导出全部'); $("#queryForm").form('submit',{ url : 'sys/user/exportData.do', onSubmit : function() { return $(this).form('validate'); }, success : function(data) { console.log('data', data); } }); } } }); }); } /** * 回调函数 * @returns */ function myCallback(name) { console.log('回调函数执行。。' + name); //重新加载datagrid $("#userListTab").datagrid('reload'); }
上面的回调函数是用于导入成功后重新加载datagrid。
第四步、导入导出的后台处理逻辑
首先,来看看UserController.java对应的代码:/** * * importTeleFraudEvent:导入数据. <br/> * * @author qiyongkang * @param request * @return * @since JDK 1.6 */ @RequestMapping @ResponseBody public ExtJsObject importData(HttpServletRequest request) { ExtJsObject result = new ExtJsObject(false, "导入不成功", null); try { String path = request.getSession().getAttribute("justpath").toString(); File f = new File(path); FileInputStream fis = new FileInputStream(f); HSSFWorkbook book = new HSSFWorkbook(fis); result = userService.importData(book); } catch (Exception e) { e.printStackTrace(); return renderObject(false, "导入不成功", null); } return result; } @RequestMapping @ResponseBody public ExtJsObject exportData(User user, HttpServletRequest request, HttpServletResponse response) { List<User> userList = null; try { if (user.getCurrentPage() != null && !user.getCurrentPage().equals(0)) { // 导出当前页 userList = userService.getPageList(user); } else { // 导出所有 userList = userService.listAll(user); } if (userList != null && userList.size() > 0) { // 开始导出,获取模板路径 String templatePath = request.getSession().getServletContext().getRealPath("template/user.xls"); // 写入工作簿 userService.exportData(userList, templatePath, response); } } catch (Exception e) { log.error("导出失败", e); return new ExtJsObject(false, "导出失败", null); } return new ExtJsObject(true, "导出成功", null); }
以及对应的service实现:
@Override public ExtJsObject importData(HSSFWorkbook workBook) { HSSFSheet sheet = workBook.getSheetAt(0); int startReadRow = 2; int lastRowNum = sheet.getLastRowNum(); for(int i = startReadRow; i <= lastRowNum; i++) { try { User entity = new User(); HSSFRow row = sheet.getRow(i); //用户编号 long id = Long.valueOf(ExcelUtil.getCellValue(row.getCell(0))); entity.setId(id); //用户姓名 String userName = ExcelUtil.getCellValue(row.getCell(1)); entity.setUserName(userName); //年龄 int age = Integer.valueOf(ExcelUtil.getCellValue(row.getCell(2))); entity.setAge(age); userMapper.addUser(entity); } catch (Exception e) { e.printStackTrace(); return new ExtJsObject(true, "导入失败", null); } } return new ExtJsObject(true, "导入成功", null); } @Override public List<User> getPageList(User user) { return userMapper.listUser(user); } @Override public void exportData(List<User> userList, String templatePath, HttpServletResponse response) throws Exception { //获取文件输入流,创建工作簿 FileInputStream fis = new FileInputStream(new File(templatePath)); HSSFWorkbook workBook = new HSSFWorkbook(fis); //设置导出文件名,并编码 String fileName = "用户信息导出_" + System.currentTimeMillis()+".xls"; fileName = URLEncoder.encode(fileName, "UTF-8"); response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\""); response.setContentType("application/octet-stream;charset=UTF-8"); //创建输出流 OutputStream outputStream = new BufferedOutputStream(response.getOutputStream()); exportUserData(workBook, userList); //删除模板页 workBook.removeSheetAt(0); //将工作簿写入输出流 workBook.write(outputStream); //关闭资源流 fis.close(); outputStream.flush(); outputStream.close(); } /** * * exportUserData:将数据写入工作簿. <br/> * * @author qiyongkang * @param workBook * @param userList * @since JDK 1.6 */ private void exportUserData(HSSFWorkbook workBook, List<User> userList) { //克隆模板sheet HSSFSheet singleSheet = workBook.cloneSheet(0); //设置克隆的sheet名称 workBook.setSheetName(1, "用户信息表"); //获取cellStyle HSSFCellStyle style = ExcelUtil.getStyle(workBook); //定义起始行,从第二行开始 int singleRowIndex = 2; //开始遍历 for (User user : userList) { try { //起始列索引 int singleColIndex=0; //创建行 HSSFRow singleRow = singleSheet.createRow(singleRowIndex++); //创建列 HSSFCell singleCell = null; //用户编号,0 singleCell=singleRow.createCell(singleColIndex++); singleCell.setCellValue(user.getId()); singleCell.setCellStyle(style); //用户姓名,1 singleCell=singleRow.createCell(singleColIndex++); singleCell.setCellValue(user.getUserName()); singleCell.setCellStyle(style); //用户年龄,2 singleCell=singleRow.createCell(singleColIndex++); singleCell.setCellValue(user.getAge()); singleCell.setCellStyle(style); } catch (Exception e) { e.printStackTrace(); continue; } } } @Override public List<User> listAll(User user) { return userMapper.listAll(user); }
再来看看模板下载以及文件上传的处理类FileHandleCtrl.java:
/** * Project Name:qyk_testSpringMVC * File Name:FileHandleCtrl.java * Package Name:com.qiyongkang.file.ctrl * Date:2015年11月1日下午3:01:05 * Copyright (c) 2015, CANNIKIN(http://http://code.taobao.org/p/cannikin/src/) All Rights Reserved. * */ package com.qiyongkang.file.ctrl; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.net.URLDecoder; import java.util.Iterator; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.fileupload.servlet.ServletFileUpload; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartHttpServletRequest; import com.qiyongkang.file.util.FileUtil; import com.qiyongkang.sys.controller.BaseController; /** * ClassName:FileHandleCtrl <br/> * Function: TODO ADD FUNCTION. <br/> * Reason: TODO ADD REASON. <br/> * Date: 2015年11月1日 下午3:01:05 <br/> * @author qiyongkang * @version * @since JDK 1.6 * @see */ @Controller @RequestMapping public class FileHandleCtrl extends BaseController { /** * * downloadtemplate:下载模板. <br/> * * @author qiyongkang * @param request * @param response * @throws IOException * @since JDK 1.6 */ @RequestMapping public void downloadtemplate(HttpServletRequest request, HttpServletResponse response) throws IOException { response.setContentType("text/html;charset=utf-8"); request.setCharacterEncoding("UTF-8"); BufferedInputStream bis = null; BufferedOutputStream bos = null; //获取template的目录路径 String templatePath = request.getSession().getServletContext().getRealPath("template"); System.out.println("templatePath:" + templatePath); String downLoadPath = templatePath + File.separator + URLDecoder.decode(new String(request.getParameter("fileName")), "UTF-8"); if("/".equals(File.separator)){ downLoadPath = downLoadPath.replace("\\", "/"); } try { File file = new File(downLoadPath); long fileLength = file.length(); response.setContentType("application/x-msdownload;"); response.setHeader("Content-disposition", "attachment; filename="+new String(file.getName().getBytes("UTF-8"), "ISO8859-1" )); response.setHeader("Content-Length", String.valueOf(fileLength)); bis = new BufferedInputStream(new FileInputStream(downLoadPath)); bos = new BufferedOutputStream(response.getOutputStream()); byte[] buff = new byte[2048]; int bytesRead; while (-1 != (bytesRead = bis.read(buff, 0, buff.length))){ bos.write(buff, 0, bytesRead); } } catch (Exception e){ e.printStackTrace(); } finally { if(bis != null){ bis.close(); } if(bos != null){ bos.close(); } } } /** * * upload:文件上传. <br/> * * @author qiyongkang * @param request * @param response * @since JDK 1.6 */ @RequestMapping public void upload(HttpServletRequest request, HttpServletResponse response) { String justpath = ""; String savePath = FileUtil.getResouseValue("uploadpath"); String filename = null; // 当前正在处理的文件分块序号 int chunk = Integer.valueOf(request.getParameter("chunk")); // 分块上传总数 int chunks = Integer.valueOf(request.getParameter("chunks")); //文件名 String resouseCode = request.getParameter("resouseCode"); // 判断当前表单是否为"multipart/form-data" boolean isMultipart = ServletFileUpload.isMultipartContent(request); try { if (isMultipart) { MultipartHttpServletRequest multiReq = (MultipartHttpServletRequest) request; Iterator<?> i = multiReq.getFileNames(); while (i.hasNext()) { multiReq.getAttribute("chunk"); MultipartFile f = multiReq.getFile((String) i.next()); InputStream input = f.getInputStream(); // 文件名 filename = f.getOriginalFilename(); if (!FileUtil.savedFileNameCache.containsKey(filename)) { FileUtil.savedFileNameCache.put(filename, FileUtil.getSavedFileName(filename)); } // 保存文件目录绝对路径 File dir = new File(savePath+"/"+resouseCode); if (!dir.isDirectory() || !dir.exists()) { dir.mkdirs(); } // 保存文件绝对路径 justpath = dir + "/"+ FileUtil.savedFileNameCache.get(filename); if (chunk == 0) { File file = new File(justpath); if (file.exists()) { file.delete(); } // 上传文件 FileUtil.uploadFile(input, justpath); } if (chunk > 0) { // 追加文件 FileUtil.uploadFile(input, justpath, true); } if (chunk + 1 == chunks || chunks == 0) { FileUtil.savedFileNameCache.remove(filename); break; } } } } catch (Exception e) { e.printStackTrace(); } request.getSession().setAttribute("justpath", justpath); } }
第五步、总结
其实,导入导出的核心逻辑就是首先上传文件,然后再对文件进行读写,而与excel操作相关的类就是工作簿类了。以上的导入导出,笔者讲得很概括,只是贴出了代码,具体的含义还得大家去领悟,由于不同的框架所使用的导入导出有可能不一致,不过大体的思路应该是一致的,有兴趣的小伙伴可以自己去尝试下,挺有意思的!
相关文章推荐
- 偏执却管用的10条Java编程技巧
- java异常
- JAVA课后作业
- 详解UML中的6大关系(关联、依赖、聚合、组合、泛化、实现)
- JAVA学习(二)
- 20131215-20140107XP、Win7下第一个Java程序(2014-01-18)
- elipse启动报错:org.eclipse.swt.SWTException: Invalid thread access
- Java基础学习19(String类型对象在内存中的分布以及与包装类之间的转换)
- java---表、栈和队列
- java类与继承
- jdbcType与javaType的对应关系
- java匿名对象_面向对象
- java封装性
- Java多态性轻松理解
- 开源框架spring AOP的深刻理解
- Spring AOP代理模式
- Java类加载器:静态变量初始化
- java关键字共50+3个
- Java反射机制(1)
- java随机生成十个数