一、效果展示

1、初始状态

导入前的页面显示:

在这里插入图片描述此时对应的数据库表:

在这里插入图片描述

2、导入的Excel文件

在这里插入图片描述

3、导入后状态

导入后的页面显示:

在这里插入图片描述
导入后的数据库表:

在这里插入图片描述

4、文件信息过滤更新

  倘若原始数据库中已存在 A 机构(将“结算代码”作为该机构的唯一标识),而我们新上传的文件中也包含了 A 机构的信息,则系统会检测出并直接更新该机构信息。

初始状态:

在这里插入图片描述传入具有相同结算代码(意味着这两家其实为同一家机构)的数据:

在这里插入图片描述
上传后的页面数据:

在这里插入图片描述
  可以看到新上传文件中的“new机构一号”与原始数据中的“机构三号”结算代码相同,因此判断出这两家实则为同一家机构,故而对“机构三号”信息进行更新(如:机构名称、地址等)。

二、实现

1、文件传递

  由于项目的前端采用的是Layui框架,因此Excel在前端的接收将以该框架为例,也可以采用其它方式/框架接收文件,比如:

<input type="file">

以Layui为例:

<div class="layui-inline" style="float: right">
  	<button type="button" id="uploadExcel" class="layui-btn layui-btn-sm layui-btn-normal"  >
        <i class="layui-icon layui-icon-upload-drag"></i>
        &nbsp上传文件
    </button>
</div>

前端接收上传的文件:

    <script type="text/javascript">
        layui.use('upload', function(){
            var $ = layui.jquery
                ,upload = layui.upload;
            upload.render({
                elem: '#uploadExcel'	//先前定义的事件id
                ,url: '/department/InputExcel' //改成您自己的上传接口
                ,accept: 'file' //普通文件
                ,exts: 'xls|xlsx' //上传的文件类型
                ,done: function(res){
                    console.log(res);
                    if(res.msg=="ok"){
                        layer.msg("文件导入成功",{icon: 1, time: 1500},function(){location.reload();});
                    }else{
                        layer.msg("文件导入失败",{icon: 2, time: 1500},function(){location.reload();});
                    }
                }
                ,error:function(){
                    layer.msg("文件导入失败",{icon: 2, time: 1500},function(){location.reload();});
                }
            });
        })
    </script>

2、Controller层

@RequestMapping(value = "/InputExcel")
    @ResponseBody
    public String InputExcel(@RequestParam("file") MultipartFile file, HttpServletRequest request) throws Exception {
        String flag = "02";// 上传标志
        System.out.println("进入controller层 导入excel");
        System.out.println("file文件:"+file);
        if (!file.isEmpty()) {
            try {
                String originalFilename = file.getOriginalFilename();// 原文件名字
                System.out.println("文件名:" + originalFilename);
                InputStream fileStream= file.getInputStream();// 获取输入流
                flag = departmentService.InputExcel(fileStream, originalFilename);
                System.out.println("controller层:上传成功,flag= "+flag);
                return "{\"msg\":\"ok\"}";
            } catch (Exception e) {
                flag = "03";// 上传出错
                e.printStackTrace();
                System.out.println("controller层:上传失败,flag= "+flag);
            }
        }
        return "{\"msg\":\"error\"}";
    }

3、Service层

 String InputExcel(InputStream fileStream, String originalFilename);

4、ServiceImplements

(1)下载并导入poi

  Java中常见的用来操作 Excel 的方式有2种:JXL和POI。JXL只能对 Excel进行操作,且只支持到 Excel 95-2000的版本。Apache POI 是用Java编写的免费开源的跨平台的 Java API,其提供对Microsoft Office格式档案读和写的功能,支持处理Excel、Word、PPT等文件。借助POI,可以方便地完成生成数据报表,数据批量上传,数据备份等工作。
  因此,需要先导入poi的jar包。

Apache POI下载官方网址: https://poi.apache.org/
POI使用相关讲解可参考博客:java Poi基础(一)

  其次,需要编写一个ExcelUtil的工具类文件,使用poi中的函数来定义自己所需要实现的Excel的功能,以方便后续方法中进行调用。

可以参考博客:
1、java使用poi读取excel
2、Java之按行、列读excel笔记

(2)判断文件类型并读取信息

  在获取到文件信息后,首先需要判断文件后缀是.xls还是.xlsx类型,并采用不同的方式读取字节流。由于在我们的ExcelUtil文件中定义的ReadExcel方法返回的是ArrayList<ArrayList>类型,因此在此处需要使用相同类型的变量进行接收。

       ArrayList<ArrayList<Object>> list;
       if (originalFilename.endsWith(".xls")) {
           System.out.println("service层:是2003版的excel");
           list = Excel.readExcel2003(fileStream);
           System.out.println("list:"+list);
       } else {
           System.out.println("service层:是2007版的excel");
           list = Excel.readExcel2007(fileStream);
           System.out.println("list:"+list);
       }

  下图可见ReadExcel的返回类型为ArrayList<ArrayList>,里面的arraylist用于存储每一行的信息,外面的arraylist将每一行组成了一个新的数组。

在这里插入图片描述
  此时,若对 list 中的内容进行调试输出,可以得到如下图所示的结果。

在这里插入图片描述

此处顺便附上java中 List 和 ArrayList 之间的区别:
1、Java中List和ArrayList的区别(加入了个人见解)
2、 java List与ArrayList区别

(3)将信息存储为键值并传递至数据库
 //  导入excel

       //建立Map<String,Map<String,Object>>
        Map<String,Map<String,Object>> oneRowInputInfos= new HashMap<String,Map<String,Object>>();
        Map<String,Object> oneRow= new HashMap<String,Object>();
        
//		遍历Excel表中的每一行
        for (int i=0,j=list.size();i<(j-1);i++){
            List<Object> row = list.get(i);			//获取当前行的数据
            
//          将当前行数据存储进oneRow  
//          row.get(1).toString():当前行第二列的数据
            oneRow.put("department_name", row.get(1).toString());
            oneRow.put("department_phone_number", row.get(2).toString());
            oneRow.put("department_address", row.get(3).toString());
            // .......此处省略部分变量
            oneRow.put("department_settlement_code", row.get(8).toString());
            // .......此处省略部分变量
            oneRow.put("depart_id", row.get(13).toString());
            
//          将当前行数据存储进oneRowinputInfos
            oneRowInputInfos.put("keys", oneRow);	
            departmentDao.InputExcel(param);
            System.out.println("service层:inputExcel成功");
        }
        return "01";
    }

下图为某一行输出的oneRowInputInfos信息:

在这里插入图片描述
传入Dao层:

 void InputExcel(Map<String, Map<String, Object>> param);

5、Mybatis:数据库信息更新

附上Mybatis中foreach语法的使用讲解:mybatis foreach标签的使用

    <!--excel导入-->
    <insert id="InputExcel" parameterType="hashmap" useGeneratedKeys="true">
        insert into db_chx_department
        <foreach collection="keys" index="key" item="value"  open="(" close=")" separator=",">
            ${key}
        </foreach>
        values
        <foreach collection="keys" item="value1" open="(" close=")" separator=",">
            #{value1}
        </foreach>
    </insert>

  至次即可完成文件的导入。

  但如果要添加文件信息的过滤更新功能,则要增加一条语句,如下。该条语句是防止新增时有重复数据而重复插入,如果设置的字段出现了重复的数据,就会更新那条重复的数据而不是新增一条。

    ON DUPLICATE key update

  如此处就将选中参数(结算代码)设置为了唯一索引,因此如果出现重复的结算代码,当前数据只会被更新,而不会被重复增加。

在这里插入图片描述
SQL完整代码:

    <!--excel导入-->
    <insert id="InputExcel" parameterType="hashmap" useGeneratedKeys="true">
        insert into db_chx_department
        <foreach collection="keys" index="key" item="value"  open="(" close=")" separator=",">
            ${key}
        </foreach>
        values
        <foreach collection="keys" item="value1" open="(" close=")" separator=",">
            #{value1}
        </foreach>
    
        ON DUPLICATE key update
        department_settlement_code=values(department_settlement_code),department_name=values(department_name),
        department_phone_number=values(department_phone_number),department_address=values(department_address),
        department_settlement_scope=values(department_settlement_scope),depart_id=values(depart_id)
	</insert>


阿巴阿巴阿巴...小萌新报道...如有错误,欢迎指正...

在这里插入图片描述


更多推荐