基于SpringMVC的Excel上传解析以及下载
简介:
本文主要基于Java的,介绍的分为三个部分,文件上传、Excel解析、文件下载,然而这三个部分都有各种各样的方法实现,原生的最通用,但也是代码量最多,本文介绍的只是基于spring,可能使用的人群没原生的那么多,仅供参考。
说明:
本文所提示的依赖的jar包指的都是主要的jar,其中还需要其他的jar未具体一一说明,这个官网上下载都会有提示相关下载的,但为了方便各位道友,点击此处打开下载页面。
文件上传:
文件上窜的思路是提交一个带文件的表单,后台动过循环遍历表单元素判断是传统数据还是文件流数据,如果是文件流数据则进行文件接收保存。
依赖的jar:commons-fileupload-1.2.1.jar。
下面贴出代码以及注释,其中加粗的是核心代码,其他的是我项目中的其他代码可以忽略。
public void upExcel(HttpServletRequest request, HttpServletResponse response) throws IllegalStateException, IOException {
// 定义返回值
Map<String, Object> resMap = new HashMap<String, Object>();
String chatid = request.getParameter("chatid");
if (chatid == null || "".equals(chatid)) {
resMap.put("resCode", "4");
resMap.put("resMsg", "群组参数错误!");
responseToPage(request, response, new JSONObject(resMap).toString());
return;
}
// 定义服务器保存的文件名(因为我的项目特殊需求,后上传的文件覆盖前面的文件,所以采用写死的文件名,这个可以根据不同的需求生成文件名)
String name = "ldry";
// 检索文件
CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver(request.getSession().getServletContext());
if(multipartResolver.isMultipart(request)) {
MultipartHttpServletRequest multiRequest = (MultipartHttpServletRequest)request;
Iterator<?> iter = multiRequest.getFileNames();
while(iter.hasNext()) {
// 遍历文件
MultipartFile file = multiRequest.getFile(iter.next().toString());
if(file != null && file.getSize() > 0) {
// 获取文件后缀
String postfix = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf(".") + 1);
// 文件类型校验
if (!"xls".equals(postfix) && !"xlsx".equals(postfix)) {
resMap.put("r esCode", "3");
resMap.put("resMsg", "请上传*.xls/*.xlsx类型的文件!");
responseToPage(request, response, new JSONObject(resMap).toString());
return;
}
// 获取文件目录
String pathLast = this.getServletContext().getRealPath("/WEB-INF/upload");
// 校验 创建文件目录
File filepath = new File(pathLast);
if (!filepath.exists() && !filepath.isDirectory()) {
System.out.println("创建目录 " + pathLast);
filepath.mkdir();
}
// 清空相关旧文件
File delFile1 = new File(filepath + "\\"+ name + ".xls");
delFile1.delete();
File delFile2 = new File(filepath + "\\"+ name + ".xlsx");
delFile2.delete();
// 拼装完整文件路径
String path = pathLast + "\\"+ name + "." + postfix;
// 保存
file.transferTo(new File(path));
// 解析EXCEL
resMap = ExcelToDB(path,chatid);
} else {
resMap.put("resCode", "3");
resMap.put("resMsg", "请勿导入空文件!");
}
}
} else {
resMap.put("resCode", "3");
resMap.put("resMsg", "未识别文件");
}
responseToPage(request, response, new JSONObject(resMap).toString());
}
HTML代码,为了能自己定义上传文件的样式,本文是吧上传文件的空间跳成透明,在其上面防止空间实现的,仅供参考,注意
enctype="multipart/form-data"
是设置表单拥有文件元素,这个必须加
encoding="multipart/form-data"
是为了兼容某可恶浏览器
<div class="filegroup">
<label class="upstyle btn btn-success">
选择文件
<form id="upFileForm" enctype="multipart/form-data" encoding="multipart/form-data">
<input type="hidden" id="chatid" name="chatid" />
<input type="file" id="filexz" name="upexcel" style="border: solid;" accept=".xls,.xlsx" />
</form>
</label>
<label class="upTip">
未选择文件
</label>
<button type="button" class="subbtn btn btn-success" οnclick="subfile()"> 保 存 </button>
</div>
css把文件元素设置成透明,其他的样式此处就不贴出来了
#filexz{
width: 100px;
position: absolute;
top: 0;
opacity: 0;
}
下面贴出js代码,红色部分是相关代码,其他部分可忽略
$(function() {
filetip();
});
// 文件选择提示
function filetip() {
$(".upstyle").on("change",function(){
var filePath = $("#filexz").val();
if(filePath.indexOf("xls") != -1 || filePath.indexOf("xlsx") != -1){
var arr = filePath.split('\\');
var fileName = arr[arr.length-1];
$(".upTip").css("color", "green");
$(".upTip").html(fileName);
}else{
$(".upTip").css("color", "red");
$(".upTip").html("请选择*.xls/*.xlsx类型的文件!");
return false;
}
});
}
// 文件上传(表单提交)
function subfile() {
var upload = window.top.layer.load(1);
var form = new FormData(document.getElementById("upFileForm"));
$.ajax({
url : "<%=basePath%>suzhou/bg/lhzh/lhzh.do?action=upExcel",
type : "post",
data : form,
processData : false,
contentType : false,
success : function(value){
window.top.layer.close(upload); // 关闭正在上传提示
var result = JSON.parse(value);
if (result.resCode == "0") {
window.top.layer.msg(result.resMsg, {icon: 1, time: 2000});
location.reload();
} else if (result.resCode == "1") {
window.top.layer.alert(result.resMsg + ".\n其他记录已导入",{icon: 6});
} else {
window.top.layer.alert(result.resMsg,{icon: 5});
}
},
error:function(e){
window.top.layer.alert("请求错误!",{icon: 2});
}
});
}
Excel解析:
要注意的是其实也可以在上传文件时解析Excel,但是个人不推荐那样,因为如果文件较大是需要一定的时间的,然而这段时间操作端要是断网或者其他原因导致上传中断会造成一系列的错误,所以推荐先把文件保存下来再解析。Excel解析其实就是读取文件然后获取Excel里面的工作表(sheet),行(row),列(cell),这个一般都可以通过遍历解决,但不同的需求可以采取不同的方式,比如我的项目只看第一张工作表的,我就不遍历工作表 ( sheet)只看第一张,列支取指定两列的我也不遍历,毕竟如果套三层循环,对效率影响还是很大的。
依赖的jar包:poi-3.16.jar。
下面贴出代码以及注释,其中加粗的是核心代码,其他的是我项目中的其他代码可以忽略
private Map<String, Object> ExcelToDB(String path,String chatid) {
Map<String, Object> resMap = new HashMap<String, Object>();
resMap.put("resCode", "0");
String resMsg = "";
File file = new File(path);
InputStream is = null;
int rowNum = 0;
try {
is = new FileInputStream(file);
Workbook wb = null;
// 获取后缀名
String postfix = path.substring(path.lastIndexOf(".") + 1);
if ("xls".equals(postfix)) {
wb = new HSSFWorkbook(is);
} else {
wb = new XSSFWorkbook(is);
}
// 遍历工作表(此处项目需要,所以只关心第一张工作表)
Sheet sheet = wb.getSheetAt(0);
List<Object[]> objs = new ArrayList<Object[]>();
// 总行数
int totalRows = sheet.getPhysicalNumberOfRows();
// 遍历当前工作表的行
for (int i = 1; i < totalRows; i++) {
int type = 0; // 人员类别 0短期值班/1长期值班
rowNum = i + 1;
Row row = sheet.getRow(i);
if (row == null) {
continue;
}
// 遍历当前行的列,此处项目需要,不做遍历
Cell c0 = row.getCell(0);
Cell c1 = row.getCell(1);
// 身份证号初处理
if (c0 == null || "".equals(c0.toString().trim())) {
resMap.put("resCode", "1");
resMsg += "第" + rowNum + "行第1列为空。";
continue;
}
c0.setCellType(CellType.STRING);
String idcard = getCellValue(c0).trim();
// 此处判断是否为空,可以用其他的判断,此处为了项目代码统一
if (StringUtils.isEmpty(idcard))
continue;
// 此处过滤掉特定的数据
if ("320666666666666666".equals(idcard))
continue;
// 值班时间以及人员类别处理
String time = null;
if (c1 == null || "".equals(c1.toString().trim())) {
type = 1;
} else {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
c1.setCellType(CellType.NUMERIC);
if (c1.getNumericCellValue() == 0.0) {
resMap.put("resCode", "1");
resMsg += "第" + rowNum + "行第2列日期格式异常。";
continue;
}
time = sdf.format(c1.getDateCellValue());
}
Object[] obj = new Object[]{time,chatid,idcard,type};
objs.add(obj);
}
int[] zbres = this.flowper_service.batchSave(objs);
if ("0".equals(resMap.get("resCode"))) {
resMsg = "导入成功";
}
resMap.put("resMsg", resMsg);
try {
is.close();
} catch (IOException e) {
is = null;
e.printStackTrace();
}
} catch (FileNotFoundException e) {
// e.printStackTrace();
resMap.put("resCode", "2");
resMap.put("resMsg", "创建文件流失败!");
} catch (IOException e) {
// e.printStackTrace();
resMap.put("resCode", "2");
resMap.put("resMsg", "读取文件失败!");
} finally {
if(is != null) {
try {
is.close();
} catch (IOException e) {
is = null;
e.printStackTrace();
}
}
}
return resMap;
}
此处为了准确解析而且解析的字段不多,所以在代码里手动转化了数据格式以及类型,下面贴出通用方法,但是由于环境不同有的不能用的可以参考上面的手动解析的
/**
* 获取Excel单元格的值
* PS:奇葩的POI。把getCellType()给过时了却没推出新的获取单元格类型方法<p>
* 导致代码在有些编译器上挺难看的。
*/
@SuppressWarnings("deprecation")
public String getCellValue(Cell cell) {
if (null != cell) {
switch (cell.getCellType()) {
case Cell.CELL_TYPE_NUMERIC: // 数字
return cell.getNumericCellValue() + "";
case Cell.CELL_TYPE_STRING: // 字符串
return cell.getStringCellValue() + "";
case Cell.CELL_TYPE_BOOLEAN: // 布尔
return cell.getBooleanCellValue() + "";
case Cell.CELL_TYPE_FORMULA: // 公式
return cell.getCellFormula() + "";
case Cell.CELL_TYPE_BLANK: // 空值
return "";
case Cell.CELL_TYPE_ERROR: // 故障
return "";
default:
return "未知类型";
}
} else {
return "";
}
}
文件下载:
文件下载就是服务端直接把文件以流的形式输出给浏览器,废话不多说,直接贴代码HTML代码
<a href="<%=basePath%>suzhou/bg/lhzh/lhzh.do?action=downExcel"><u><I></i>点击这里</I></u></a>
Java后台代码
public void downExcel(HttpServletRequest request, HttpServletResponse response) throws IOException {
JSONObject res = new JSONObject();
res.put("errcode", "0");
res.put("errmsg", "下载成功");
String fileName = "值班表示例.xls";
// 此处对文件名进行编码一方乱码的,可视情况使用
// fileName = new String(fileName.getBytes("iso8859-1"),"UTF-8");
// 文件路径
String path = this.getServletContext().getRealPath("/WEB-INF/download");
File file = new File(path + "\\" + fileName);
if(!file.exists()){
res.put("errcode", "1");
res.put("errmsg", "您要下载的资源已被删除!");
responseToPage(request, response, res.toString());
return;
}
// 设置响应头,让浏览器下载文件
response.setHeader("content-disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
// 文件放到输入流
FileInputStream in = new FileInputStream(path + "\\" + fileName);
// 创建输出流
OutputStream out = response.getOutputStream();
// 创建缓冲区
byte buffer[] = new byte[1024];
int len = 0;
while ((len = in.read(buffer)) > 0) {
// 输出缓冲区的内容到浏览器,实现文件下载
out.write(buffer, 0, len);
}
in.close();
out.close();
}
本文介绍就到此结束了,希望对您有所帮助,也感谢您的阅读和支持