如何实现多表格(excel)导入,并在页面展示?

问题遇到的现象和发生背景

现有30多个省份的excel模板数据,每个excel数据的列数不统一,个别字段也不一样,现要求把数据导入系统中,后台能控制前台每个省份的字段是否展示?当按照名称搜索时,能搜索所有省份的数据。 问题是,我该如何设计这个表结构?导入的时候,能尽量少修改原模板数据,方便用户操作分别导入不同省份的数据

我的解答思路和尝试过的方法

尝试过从30多个excel模板中抽取相同部分,形成一个excel模板数据,全部都按照统一模板导入,但是这样用户操作太累了,每次导入都要修改一下原数据形成统一模板才能导入,请教一下有什么好的办法?

模板数据是要统一的,就抽象导入接口,实现类就做差异的事情

方法只有2种

  • 按统一版本导入数据
  • 程序根据不同模板解析数据

第1种你已经试了,程序省事了,用户麻烦了。
那么就换第2种,用户体验不变,程序支持多种不同模板的导入,
根据不同的省份按对应的模板进行解析即可,只要程序做完了,也就一次性工作量。

如有帮助,请采纳,十分感谢!

这个功能分为两个步骤:

  1. 解析Excel数据
  2. 保存数据到数据库

现在有两个Excel表,Excel_A,Excel_B 代表两个省的模板数据,且“列数不统一,个别字段也不一样”:

Excel_A:

nametel
医生120
警察110

Excel_B:

nameaddrage
张三唐山40
李四唐山45

一、第一步,解析数据:

这里使用POI来操作Excel表,读取单元格数据。解析数据并不需要依赖统一的表格模板,一个方法及可。通过POI的api可以直接拿到Excel的行数和列数。然后,便利每一行的每个单元,读取每个单元格以键值对保存到集合中,key为第一行列名,value为每个单元格的值。
读取到的数据结构为:

List<Map<String, String>>;
// list[i] 为第i+1行的数据(第一行是字段名)
// Map 如:{"name: 张三", "addr: 唐山", "age: 40"}

工具类依赖

        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi</artifactId>
            <version>3.10-FINAL</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>3.10-FINAL</version>
        </dependency>

工具类代码:

import com.fasterxml.jackson.databind.exc.InvalidFormatException;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.util.StringUtils;

import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @Date 2022/6/13 13:06
 * @Version 1.0
 */
public class ExcelUtil {
    public static List<Map<String, Object>> importExcel(InputStream inputstream)
            throws IOException, InvalidFormatException {
        Workbook workbook = new XSSFWorkbook(inputstream);
        Sheet sheet = workbook.getSheetAt(0);
        int physicalNumberofRows = sheet.getPhysicalNumberOfRows(); //封装对象List<map<map<String, Object>>>
        List<Map<String, Object>> list = new ArrayList<>();
        Row row1 = sheet.getRow(0);
        int physicalNumberofcells = row1.getPhysicalNumberOfCells();
        for (int j = 0; j < physicalNumberofRows; j++) {
            Map<String, Object> map = new HashMap<>();
            if (j == 0) {
                continue;
            }
            Row row = sheet.getRow(j);
            if (row == null) {
                return list;
            }
            int isLastRowFlag = 0;
            for (int cellnum = 0; cellnum < physicalNumberofcells; cellnum++) {
                Cell cell = row.getCell(cellnum);
                //校验获取表格中的值
                String cellvalue = getValue(cell);
                String key = getValue(row1.getCell(cellnum));
                map.put(key, cellvalue);
                if (!StringUtils.isEmpty(cellvalue)) {
                    isLastRowFlag++;
                }
            }
            if (isLastRowFlag == 0) { //该行无数据, 视为最后一行
                return list;
            }
            list.add(map);
        }
        return list;
    }

    public static String getValue (Cell cell){
        String cellvalue = "";
        if (cell == null) {
            return cellvalue;
        }
        //把数字当成String来读, 避免出现1读成1.0的情况
        if (cell.getCellType() == cell.CELL_TYPE_NUMERIC) {
            cell.setCellType(cell.CELL_TYPE_STRING);
        }
        //判断数据的类型
        switch (cell.getCellType()) {
            case Cell.CELL_TYPE_NUMERIC:
                //数字
                cellvalue = String.valueOf(cell.getNumericCellValue());
                break;
            case Cell.CELL_TYPE_STRING:
                // 字符串
                cellvalue = String.valueOf(cell.getStringCellValue());
                break;
            case Cell.CELL_TYPE_BLANK:
                // 空
                cellvalue = null;
                break;
            case Cell.CELL_TYPE_BOOLEAN:
                cellvalue = String.valueOf(cell.getBooleanCellValue());
                break;
            default:
                cellvalue = "未知类型";
                break;
        }
            return cellvalue;
    }

// 测试工具类
    public static void main(String[] args) throws IOException {
        InputStream inputStream = new BufferedInputStream(
                new FileInputStream(
                        new File("C:\\Users\\wang\\Desktop\\test.xlsx")));
        List<Map<String, Object>> list = ExcelUtil.importExcel(inputStream);
        for (Map<String, Object> map : list){
            for (Map.Entry entry : map.entrySet()){
                System.out.println(entry.getKey()+"-"+entry.getValue());
            }
        }
    }
}

一、第二步,保存数据到数据库:
数据存储,一个表即可,字段即为30多个省份的excel模板字段的集合。如Excel_A,Excel_B两个表,表结构可创建如下:

nameaddragetel
张三唐山40null
李四唐山45null
医生nullnull120
警察nullnull110

如果该省的模板没有某些字段,设为null。保存在一个表,这样搜索也方便。

有过类似的问题,我们是这样解决的,看一下能否参照:

  1. 对于存储数据,创建两个表,一个共同情报A表(或添加重要的个性项目),一个个性信息B表,把上传的多省份数据纵向切分;
  2. 画面做整体检索时对象A表,如遇到特性查询或操作,处理B表;
  3. 如果数据复杂,需要创建一个B表的类别C表,用来区分各省Excel文件种类,及B表的存储种类。

补充说明:
B表因为是个性表,存在数据存储的浪费,但这样逻辑简单;具体情况需要看具体的需求。