在做web开发时,我们经常会用到数据表格的导入导出功能,这样可以帮我们节省人工操作的时间,极大提高办事效率,下面,直入正题:
笔者所做的导入导出是针对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操作相关的类就是工作簿类了。
以上的导入导出,笔者讲得很概括,只是贴出了代码,具体的含义还得大家去领悟,由于不同的框架所使用的导入导出有可能不一致,不过大体的思路应该是一致的,有兴趣的小伙伴可以自己去尝试下,挺有意思的!
来源:CSDN
作者:qiyongkang520
链接:https://blog.csdn.net/qiyongkang520/article/details/49592725