layui+poi-Java实现导入导出excel文件

2023-10-27

目录


需求说明

项目背景:springmvc架构下,前端jsp(引入layui框架样式)需要实现导出excel文件(模板),用户填写内容后,再上传进系统,系统解析保存excel文件中的数据。


一、实现思路

1、【模板制作】提前做好一个excel文件,可以用Excel自带的功能限制单元格的格式:例如某列只允许填写纯数字,某列不允许为空等等,以便用户填写错误。

2、【导出模板】把模板放到项目中,前端请求后,后端直接下载模板。

3、【导入】使用layui框架实现前端,java后端读取excel文件,POI框架操作文件,验证数据正确性等。

二、前端代码

1.引入layui

<script src="${xxxx}/layui-v2.4.5/layui/layui.all.js" charset="utf-8"></script>

2.隐藏部分内容

有一弹窗,点击【导入xx】按钮之前不显示。点击【导入xx】按钮后弹出:

1)静态页面代码:

<body>
	<div style="display: none;padding: 5px" id="showUploadExcelDiv">
			<form action="" method="post" class="layui-form layui-form-pane" id="exportFrm" lay-filter="exportFrm">
				<div class="layui-upload" >
					<button type="button" style="margin-top: 20px;margin-left: 20px" class="layui-btn layui-btn-warm" 
						 onclick="downloadTemplate()">下载模板</button>
					<button type="button" style="margin-top: 20px;margin-left: 20px" class="layui-btn layui-btn-normal" 
						id="chooseFile">选择文件</button>
					<button type="button" style="margin-top: 20px" class="layui-btn"
						id="uploadFle">开始上传</button>
				</div>
			</form>
		</div>
		<table class="layui-hide" id="test" lay-filter="test"></table>
	<form>
		<div class="panel-body">
			<div class="row-fluid">
				<div class="span3">	
					<span class="label-none"> <br/></span>
					<button type="button" id="uploadFile" class="layui-btn layui-btn-sm"   onclick="upload();" >导入</button>
				</div> 
			</div>
		</div>
    </form>
<body>

2)js(/jquery)代码:点击【导入xx】按钮的js:弹出上面隐藏的内容

        function upload(){
			 layer.open({
	                type: 1,
	                area: ['800px','150px'],
	                fixed: false,
	                title: '导入数据',
	                content: $("#showUploadExcelDiv"),
	                end: function () {
	                    location.reload();
	                }
	            })
		}

3)、效果如下

 

3、下载模板js

function downloadTemplate(){
    //后端下载地址
    location.href="${xxx}/xxx/yyy/downloadTemplate";
}

4、选择文件、上传js 

	<script>
	/* 导入 */
	layui.use(['form', 'layedit','upload'], function(){
	    var form = layui.form,
	    $ = layui.jquery,
	    layer = layui.layer,
	    layedit = layui.layedit,
	    upload=layui.upload;

	    upload.render({
            elem: "#chooseFile",   //导入id
            //这里可以带一些页面上想传给后台的参数
            data:{
                id:function(){
                	var id = $("#id").val();
                    return id;
                },
                year:function(){
                	var year = $("#year").val();
                    return year;
                }
            },
            url: '${xx}/xxx/yyy/importExcel?year='+year+"&id="+id,
            // 与bindAction一起使用,不直接上传,当点击#uploadFle按钮时才上传
            auto: false,    
            bindAction: '#uploadFle',
            accept: "file",
            size: 10240,
            exts: 'xls|xlsx', 
            /* 上传完毕回调 */
            done: function (res) {
                /* 后端传code值:1代表成功,0代表失败 */
            	var code = res.code;
            	if(code == "1"){
            		top.$.jBox.tip("导入成功!");
            		//可以跳转到某页面
            		//location.href="${xxx}/xxx/yyy/form;
                    //可以刷新本页面
            		//window.location.reload()
            	}else{
                    //失败后弹出页面,上面附有失败原因(后端写好传来)
            		layer.confirm(res.msg, {title:'导入失败,系统提示'},function(index){
            			layer.close(index);
            			window.location.reload()
            		});
            	}
            },
            error: function(){
                //请求异常回调
                top.$.jBox.tip("服务器异常,请稍后再试");
                window.location.reload()
            }
        });
	 })
</script>

效果展示:


 三、后端代码

1、下载模板:


    /**
	 * 文件模板下载
	 * @param fileModleId
	 * @param request
	 * @param response
	 * @throws Exception
	 */
	@RequestMapping(value = "downloadTemplate")
	public void downloadTemplate(HttpServletResponse response) throws Exception {
        //获取项目中模板为输入流
		InputStream is=Thread.currentThread().getContextClassLoader().getResourceAsStream("templates/xxx/yyyy/DetailTemp.xlsx");
        //利用poi框架导出文件
        //org.apache.poi.xssf.usermodel.XSSFWorkbook;
		XSSFWorkbook wb0 = new XSSFWorkbook(is);
		try {
			OutputStream out = response.getOutputStream();
			response.setHeader("Content-disposition", "attachment;filename="+ new String( "导入成本明细清单模板.xlsx".getBytes("gb2312"), "ISO8859-1" ));
			response.setContentType("application/msexcel;charset=UTF-8");
			wb0.write(out);
			out.flush();
			out.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

2、导入文件 

1)controller层

	/**
	 * 导入excel文件
	 * @param file
	 * @return
	 */
	@RequestMapping(value = "importExcel", method = RequestMethod.POST, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    @ResponseBody
    public HashMap<String, Object> importData(@RequestPart(value = "file") MultipartFile file,HttpServletRequest request) {
		
		//获取导入时传来的参数:可做业务处理:略
		String year = request.getParameter("year");
		String id = request.getParameter("id");
		
        //构建返回前端值,此处使用HashMap<String, Object>
        //其中key为code value为1代表成功,value为0代表失败
        //   key为msg,value写入具体错误信息是什么
		HashMap<String, Object> resultMap = null;
		try {
    		InputStream inputStream = file.getInputStream();
            //写个工具类,处理excel 的校验、保存:返回值直接是可以对接前端的
            resultMap = util.readExcel(inputStream, file.getOriginalFilename(),new XXXEntity());
            inputStream.close();
            return resultMap;
		} catch (Exception e) {
			System.out.print("[导入异常]"+e.getMessage());
			resultMap.put("code", "0");
			resultMap.put("msg", "读取文件异常!");
			return resultMap;
	    }
    }

2)处理文件工具类

@Service
public class ExcelUtilForXXXImport {
	private static final Logger logger = LoggerFactory.getLogger(ExcelUtilForXXXImport.class);

	@Autowired
	private XXXEntityService xxxEntityService;
	
	/**
     * 处理上传的文件
     *
     * @param in
     * @param fileName
     * @return
     * @throws Exception
     */
    public HashMap<String, Object> readExcel(InputStream in, String fileName,XXXEntity xxxEntity) throws Exception {
        //构建返回值
    	HashMap<String, Object> hashMap=new HashMap<String, Object>();
        //创建Excel工作薄
        try {
        	//判断文件格式是否为excel,是否为空
            //详见:3)后端校验文件格式的方法
        	Workbook work = getWorkbook(in, fileName);
            if (null == work) {
                throw new Exception("创建Excel工作薄为空!");
            }

            //读第一页
            Sheet sheet = work.getSheetAt(0);

            //step1: 验证导入文件的标题头是否合法
    		String[] columnName = {"序号", "月份",...};
            //详见 : 4)校验表格标题行方法
    		String resultString=verificationExcelHeadLine(sheet, columnName);
            //有返回信息代表有错误,没有返回信息代表验证通过
    		if (!resultString.equals("")) {
    			hashMap.put("code", "0");
    			hashMap.put("msg", "请使用正确模板上传excel文件。"+resultString);
    			return hashMap;
    		}

    		//step2: 验证导入文件的标题 列 是否合法
    		String[] colName = {"第一月", "第二月",。。。};
            //详见: 6)判断列标题头是否正确
    		String resultStr = verificationExcelHeadLineTwo(sheet, colName);
            //有返回信息代表有错误,没有返回信息代表验证通过
    		if (!resultStr.equals("")) {
    			hashMap.put("code", "0");
    			hashMap.put("msg", "请使用正确模板上传excel文件。"+resultStr);
    			return hashMap;
    		}

    		//step3: 验证数据是否为空,长度是否过长,是否为纯数字。。。
            // 详见7)校验数据是否为空
    		String verificationDateResultString = verificationDate(sheet);
    		if (!verificationDateResultString.equals("")) {
    			hashMap.put("code", "0");
    			hashMap.put("msg", verificationDateResultString);
    			return hashMap;
    		}

    		//step4: 文件内容合法,做业务逻辑处理(保存)
            // 详见9)做业务逻辑处理(保存数据)
    		String saveResultString = insertData(sheet,xxxEntity);
    		if (!saveResultString.equals("")) {
    			hashMap.put("code", "0");
    			hashMap.put("msg", saveResultString);
    			return hashMap;
    		}

            //以上都顺利进行,返回前端:导入成功
    		hashMap.put("code", "1");
	        hashMap.put("msg", "导入成功!");
	        return hashMap;
		} catch (Exception e) {
			System.out.print("[读取excel异常]"+e.getMessage());
			hashMap.put("code", "0");
	        hashMap.put("msg", "读取excel异常");
	        return hashMap;
		}
        
    }

}

3)后端校验文件格式的方法

	/**
     * 后端判断文件格式方式
     *
     * @param inStr
     * @param fileName
     * @return
     * @throws Exception
     */
    public static Workbook getWorkbook(InputStream inStr, String fileName) throws Exception {
        Workbook workbook = null;
        String fileType = fileName.substring(fileName.lastIndexOf("."));
        if (".xls".equals(fileType)) {
            workbook = new HSSFWorkbook(inStr);
        } else if (".xlsx".equals(fileType)) {
            workbook = new XSSFWorkbook(inStr);
        } else {
            throw new Exception("请上传excel文件!");
        }
        return workbook;
    }

4)校验表格标题行方法

    /***
     * 校验导入的列表Excel文件标题行是否为标准行
     */
    public static String verificationExcelHeadLine(Sheet sheet, String[] columnName) throws Exception {
    	String result = "";
    	try {
            //获取第一行:标题行
    		Row row = sheet.getRow(0);
            
    		if (row != null && row.getLastCellNum() >= columnName.length) {
    			int lastCellNum = row.getLastCellNum();
                //循环遍历指定的数组内容与文件中内容一一对比
    			for (int idx = 0; idx < lastCellNum; idx++) {
                    //将格子中的内容转为java的string类型的方法
                    //详见5)将excel格子中的内容转为java的string类型的方法
    				String value = getStringCellValue(row.getCell(idx));
    				if (idx < 4) {
    					if (value == null || !columnName[idx].equals(value)) {
    						result = result+"标题行第" + (idx + 1) + "列名称错误!";
    					}
    				} else {
    					if (idx == 4) {
    						if (value == null) {
    							result = result+"标题标准表格表头不一致";
    						}
    					}
    				}
    			}
    		} else {
    			result = "上传文件首行不能为空或与标准表格表头不一致!";
    		}
    	} catch (Exception ex) {
    		logger.info("【导入成本】校验数据异常");
    	}
    	return result;
    }

5)将excel格子中的内容转为java的string类型的方法 

    /**
     * 获取单元格数据内容为字符串类型的数据
     * 
     * @param cell Excel单元格
     * @return String 单元格数据内容
     */
    private static String getStringCellValue(Cell cell) {
        String strCell = "";
        switch (cell.getCellType()) {
        case Cell.CELL_TYPE_STRING:
            strCell = cell.getStringCellValue();
            break;
        case Cell.CELL_TYPE_NUMERIC:
            strCell = String.valueOf(cell.getNumericCellValue());
            break;
        case Cell.CELL_TYPE_BOOLEAN:
            strCell = String.valueOf(cell.getBooleanCellValue());
            break;
        case Cell.CELL_TYPE_BLANK:
            strCell = "";
            break;
        default:
            strCell = "";
            break;
        }
        if (strCell.equals("") || strCell == null) {
            return "";
        }
        if (cell == null) {
            return "";
        }
        return strCell;
    }
    

6)判断列标题头是否正确

    public String verificationExcelHeadLineTwo(Sheet sheet, String[] columnName) {
    	String result = "";
    	try {
    		int allRowNum = sheet.getLastRowNum();
    		for(int i = 1; i <= allRowNum; i++) {
    			Row row = sheet.getRow(i);
    			if (row != null && row.getCell(1) != null) {
    				String value = getStringCellValue(row.getCell(1));
    				if (value == null || !columnName[i-1].equals(value)) {
						result = result+"第" +i+"行第2列名称错误!";
					}
        		} else {
        			result = "请选择正确模板!";
        		}
    		}
    	} catch (Exception ex) {
    		logger.info("【导入成本】校验数据异常");
    	}
    	return result;
    }

 7)校验数据是否为空

	/**
     * 校验数据是否为纯数字,是否为空
     * @param sheet
     * @return
     */
	
	private String verificationDate(Sheet sheet) {
		String result = "";
		int allRowNum = sheet.getLastRowNum();
		if(allRowNum != 12) {
			result = "数据行数不为12行,请检查模板;";
			return result;
		}
    	try {
    		//循环行
    		for(int i = 1; i <= sheet.getLastRowNum(); i++) {
    			Row row = sheet.getRow(i);
    			int allCellNum = row.getLastCellNum();
        		if (row != null &&  allCellNum == 9) {
        			//循环列
        			for (int idx = 0; idx < allCellNum; idx++) {
        				String value = getStringCellValue(row.getCell(idx));
        				if(!StringUtils.isNotEmpty(value)) {
        					result = result+"第"+i+"行"+"第"+idx+"列为空;";
        				}else {
        					//第二列以后为纯数字
        					if(idx>=2) {
                                //isNumeric为校验是否为数字的方法
                                //详见:8)校验是否为纯数字
        						if(!isNumeric(value)) {
        							result = result+"第"+i+"行"+"第"+idx+"列不是数字格式;";
        						}
        					}
        				}
        			}
        		} else {
        			result = "数据列数不为8列,请检查模板;";
        			return result;
        		}
    		}
    	} catch (Exception ex) {
    		 logger.info("【导入成本】校验数据异常");
    	}
    	return result;
	}
	 

8)校验是否为纯数字 :心机boy的方法

    /**
     * 判断是否为数字:直接去创建BigDecimal,失败就是非数字,BigDecimal校验的比我们厉害
     * @param str
     * @return 
     */
    public static boolean isNumeric(String str) {
        String bigStr;
        try {
            bigStr = new BigDecimal(str).toString();
        } catch (Exception e) {
            return false;//异常 说明包含非数字。
        }
        return true;
    }

9)做业务逻辑处理(保存数据)



    private String insertData(Sheet sheet,XXXEntity xxxEntity) {
    	String result = "";
    	
    	//构建子表数据
    	List<XXXEntity> xxxEntityList = new ArrayList<XXXEntity>();
    	Row row = null;
    	try {
    		for (int i =1; i <= sheet.getLastRowNum(); i++) {
                row = sheet.getRow(i);
                XXXEntity entity = new XXXEntity();
                
                entity.setMonth(getStringCellValue(row.getCell(0)));
                entity.setXXX(
                    new BigDecimal(
                        getStringCellValue(row.getCell(1))));
                //...
                xxxEntityList.add(entity);
            }
   
        	xxxEntityService.save(jCCostManager);
    	}catch(Exception e) {
    		result = "数据格式异常无法保存。";
    		return result;
    	}
    	
    	return result;
    }
    

总结

        以上可以实现简单导入导出excel,如有错误,请指正!

        希望广大网友有更简便的方法或框架可以提供~

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

layui+poi-Java实现导入导出excel文件 的相关文章

随机推荐

  • 531-C++迭代器失效问题及解决方法

    初步在自定义vector类中实现迭代器 迭代器示意图 为什么方式都是一样 因为迭代器遍历完当前元素跳到下一个元素 底层数据结构的具体的遍历方式都封装在这个迭代器的 运算符函数了 所以 作为使用方 我们不需要知道底层的数据结构原理 我们只知道
  • 寄存器堆计算机组成实验,杭电计算机组成原理寄存器堆设计实验4

    杭电计算机组成原理寄存器堆设计实验4 5页 本资源提供全文预览 点击全文预览即可全文预览 如果喜欢文档就下载吧 查找使用更方便哦 9 9 积分 杭州电子科技大学计算机学院实验报告课程名称 计算机组成原理 实验项目 寄存器堆设计实验姓班级 指
  • C语言实现斐波那契数列

    先来说说啥是斐波那契数列 它是一个前两位都为1 从第三位开始 后一位为前两位之和的递增数列 也就是 1 1 2 3 5 8 13 21 34 55 89 我们可以发现 如果你要找第n位的数 其实它就等于第 n 1 位和第 n 2 位之和 那
  • IOS 关于 NSUserDefault

    转载 并不是所有的东西都能往里放的 NSUserDefaults只支持 NSString NSNumber NSDate NSArray NSDictionary NSUserDefaults的方法中用来记录一下永久保留的数据非常方便 不需
  • 大数据手册(Spark)--Spark机器学习(PySpark版)

    文章目录 MLlib ML 常见的特征转换 模型拟合和描述 超参调优 Spark安装配置 Spark基本概念 Spark基础知识 PySpark版 Spark机器学习 PySpark版 Spark流数据处理 PySpark版 MLlib A
  • java创建任意长度空格字符串_输入任意长的一个字符串,统计其字母、数字、空格及其他字符的数量。...

    标签 思路 简单的利用一个多重 if 结构就可以解决 CODE import java util Scanner public class Character public static void main String args Syst
  • 经纬度正则表达式

    经度 180 0 180 0 整数部分为0 180 输入1到8位小数 0 d 1 8 1 9 d d 1 8 1 0 7 d 1 d 1 8 180 0 1 8 纬度 90 0 90 0 整数部分为0 90 输入1到8位小数 0 1 8 d
  • ant上传组件upload,前端读取文件、创建文件

    业务需求是用户上传文件后 还能修改文件内容 最后保存修改后的文件 实现方式 1 前端读取文件内容后显示在代码编辑器中 方便用户修改 2 点保存按钮时 拿到改变的文件内容后 创建新的文件流 提交给后端 这里演示的是JSON文件数据 选择文件
  • 图显系统DRM CRTC完全解析

    目录 CRTC 工作原理和意义 CRTC 模块的初始化和功能 0 引言 DRM 下的 CRTC 代表 RGB 数据管道 从 drm plane 接收像素数据并将其混合到一起 传输给下级显示设备 drm encoder 由 drm displ
  • 原址删除有序数组重复整数

    给定一个有序数组 原址删除重复超过两次的整数 本笔记适合熟悉Python列表list的 coder 翻阅 学习的细节是欢悦的历程 Python 官网 https www python org Free 大咖免费 圣经 教程 python 完
  • Spring中bean创建的生命周期

    1 推断构造方法 2 实例化 3 依赖注入 4 处理Award回调beanFatoryAware 5 初始化前 处理 postConstruct注解 6 初始化 处理iniyializingBean接口 7 初始化前 进行aop
  • Distributed Database System —— Multi-raft协议介绍

    文章目录 Multi Raft协议 Multi Raft需要解决的问题 Multi Raft实现细节 Cockroach Multi Raft Raft Consistency of Range Replicas Range Leaders
  • SQL备份表数据

    1 情况说明 对某个表 需要进行某些删除或修改操作测试 但也需要数据还原 所以需要备份表中数据 2 思路分析 一般操作 将表A所有的数据 备份到新建表B中 若有其他更屌的操作 请告诉我 万分感谢 3 具体SQL实现 库类型说明 SQL SE
  • Java编程--IO流(Ⅱ 字节流)

    Java编程 IO流 字节流 File类虽然可以操作文件 但是并不是操作文件的内容 若要进行文件内容的操作只能通过两种途径完成 字节流和字符流 若要进行输入及输出操作一般都会按照如下的步骤进行 以文件操作为例 1 通过File类定义一个要操
  • 自定义SonarQube Java规则

    自定义SonarQube扫描Java Rule 在介绍如何自定义规则之前 先介绍一下这几个产品 SonarQube 代码质量管理平台 PMD 源代码分析器 FindBugs Java源代码分析器 查找代码Bug Sonar PMD Sona
  • PhotonServer的使用

    https blog csdn net a962035 article details 80713726
  • 一些编译器与解释器的理解

    概述 在19年拜读完Python解释器以及PHP部分解释器并未完全理解 当时主要关心于几个问题 解释器在干嘛 为什么 怎么干的 导致出现区域性片面的理解 直到今年读到内核才逐渐理解 碰巧看到LLVM 大佬思路就是清晰 简单描述个人的理解 如
  • k8s中的有状态,无状态,pv、pvc等

    数据库是一个典型的有状态服务 他的部署和无状态服务是不一样的 PostgresSQL 基于Kubernetes部署PostgresSQL CSDN博客 一 创建SC PV和PVC存储对象 二 部署PostgresSQL Volume Kub
  • dell进入u盘启动模式_uefi不识别u盘怎么办 uefi不识别u盘方法【图文详解】

    现代的电脑配置更新换代都很频繁 从以前的bios主板到现在的uefi主板配置都有很大的进步 而这种新型uefi配置也给新用户装系统带来麻烦 有些用户用u盘启动盘装系统发现uefi不识别u盘 uefi bios识别不了u盘的原因其实就是Lau
  • layui+poi-Java实现导入导出excel文件

    目录 需求说明 一 实现思路 二 前端代码 1 引入layui 2 隐藏部分内容 1 静态页面代码 2 js jquery 代码 点击 导入xx 按钮的js 弹出上面隐藏的内容 3 效果如下 3 下载模板js 4 选择文件 上传js 三 后