1. 程式人生 > 實用技巧 >SpringBoot解析excel把資料儲存到資料庫

SpringBoot解析excel把資料儲存到資料庫

一、建立一個spring boot專案

1.1 開發工具 idea

1.2 jdk 1.8

1.3 具體專案搭建流程可以閱讀我的另一篇部落格(建立spring boot專案

1.4 整體結構

二、搭建spring boot開發環境

2.1 新增pom檔案

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation
="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.4.RELEASE</
version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.liyh</groupId> <artifactId>springboot_excel</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <
name>springboot_excel</name> <description>study springboot_excel</description> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> </dependency> <!--mysql驅動--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <!--jdbc 資料庫連線--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <!-- 引入阿里資料庫連線池 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.6</version> </dependency> <!--lombok--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <!-- mybatisPlus 核心庫 --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.2.0</version> </dependency> <dependency> <groupId>net.sourceforge.nekohtml</groupId> <artifactId>nekohtml</artifactId> <version>1.9.15</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>4.1.2</version> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.10</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> <resources> <resource> <directory>src/main/java</directory> <includes> <include>**/*.xml</include> </includes> </resource> <resource> <directory>src/main/resources</directory> </resource> </resources> </build> </project>
pom.xml

2.2 配置application.yml檔案

# 配置埠
server:
  port: 8086

spring:
  # 配置資料來源
  datasource:
    url: jdbc:mysql://localhost:3306/db1?useSSL=false&serverTimezone=UTC
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver

  thymeleaf:
    mode: LEGACYHTML5
    # 取消模板檔案快取
    cache: false

  #設定thymeleaf檔案路徑 預設為src/main/resources/templates
  freemarker:
    template-loader-path: classpath:/templates

  #設定靜態檔案路徑,js,css等
  mvc:
    static-path-pattern: /static/**

  servlet:
    multipart:
      # 設定單個檔案大小
      max-file-size: 200MB
      # 設定單次請求檔案的總大小
      max-request-size: 200MB

# mybatis-plus相關配置
mybatis-plus:
  # xml掃描,多個目錄用逗號或者分號分隔(告訴 Mapper 所對應的 XML 檔案位置)
  mapper-locations: classpath*:com/liyh/mapper/xml/*.xml
  configuration:
    # 是否開啟自動駝峰命名規則對映:從資料庫列名到Java屬性駝峰命名的類似對映
    map-underscore-to-camel-case: true

#列印sql,儲存到檔案
logging:
  level:
    com.liyh.mapper: debug

2.3 編寫摸板檔案

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<hr/>
<p>摸板下載</p>
<a href="/excel/download">下載摸板</a>
<hr/>
<p>檔案上傳</p>
<form action="/excel/import" method="POST" enctype="multipart/form-data">
    檔案:<input type="file" name="file"/>
    <input type="submit"/>
</form>
<hr/>
</body>
</html>

2.4 建立IndexController,FileController

package com.liyh.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

/**
 * @Author: liyh
 * @Date: 2020/10/23 17:33
 */

@Controller
public class IndexController {

    @RequestMapping("/")
    public String index()
    {
        return "index";
    }
}

package com.liyh.controller;

import com.liyh.entity.Result;
import com.liyh.service.ExcelService;
import com.liyh.utils.ExcelTool;
import com.liyh.utils.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 介面
 *
 * @Author: liyh
 * @Date: 2020/10/23 17:05
 */

@RestController
@RequestMapping("/excel")
public class ExcelController {

    Logger logger = LoggerFactory.getLogger(ExcelController.class);

    @Autowired
    private ExcelService excelService;

    @PostMapping("/import")
    public Result importProject(MultipartFile file) {
        String postfix = ExcelTool.getPostfix(file.getOriginalFilename());

        if (!"xlsx".equals(postfix) && !"xls".equals(postfix)) {
            return Result.error("匯入失敗,請選擇正確的檔案格式支援xlsx或xls");
        }
        return excelService.importProject(file);
    }

    @GetMapping("/download")
    public String downloadFile(HttpServletRequest request, HttpServletResponse response) {
        String fileName = "template.xlsx";
        String result = FileUtils.downloadFiles(request, response, fileName);
        if (request == null) {
            return null;
        }
        return result;
    }
}

2.4 檔案工具類

package com.liyh.utils;

import org.springframework.core.io.ClassPathResource;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;

/**
 * @Author: liyh
 * @Date: 2020/11/4 16:10
 */

public class FileUtils {

    /**
     * 下載檔案
     * @param request
     * @param response
     * @param fileName
     * @return
     * @throws IOException
     */
    public static String downloadFiles(HttpServletRequest request, HttpServletResponse response, String fileName){

        if (StringUtils.isEmpty(fileName)) {
            return "檔名稱為空";
        }

        //設定檔案路徑
        ClassPathResource classPathResource = new ClassPathResource("templates/" + fileName);
        File file = null;
        try {
            file = classPathResource.getFile();
        } catch (IOException e) {
            e.printStackTrace();
            return "檔案不存在";
        }

        response.setHeader("content-type", "application/octet-stream");
        // 設定強制下載不開啟
        response.setContentType("application/force-download");
        // 設定檔名
        response.addHeader("Content-Disposition", "attachment;fileName=" + fileName);

        byte[] buffer = new byte[1024];
        InputStream fis = null;
        BufferedInputStream bis = null;

        try {
            fis = new FileInputStream(file);
            bis = new BufferedInputStream(fis);
            OutputStream os = response.getOutputStream();
            int i = bis.read(buffer);
            while (i != -1) {
                os.write(buffer, 0, i);
                i = bis.read(buffer);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (bis != null) {
                try {
                    bis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return "檔案下載成功";
    }

    /**
     * 判斷檔案大小
     *
     * @param file  檔案
     * @param size  限制大小
     * @param unit  限制單位(B,K,M,G)
     * @return
     */
    public static boolean checkFileSize(MultipartFile file, int size, String unit) {
        if (file.isEmpty() || StringUtils.isEmpty(size) || StringUtils.isEmpty(unit)) {
            return false;
        }
        long len = file.getSize();
        double fileSize = 0;
        if ("B".equals(unit.toUpperCase())) {
            fileSize = (double) len;
        } else if ("K".equals(unit.toUpperCase())) {
            fileSize = (double) len / 1024;
        } else if ("M".equals(unit.toUpperCase())) {
            fileSize = (double) len / 1048576;
        } else if ("G".equals(unit.toUpperCase())) {
            fileSize = (double) len / 1073741824;
        }
        if (fileSize > size) {
            return false;
        }
        return true;
    }
}
FileUtils

2.5 service

package com.liyh.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.liyh.entity.ProjectItem;
import com.liyh.entity.Result;
import com.liyh.mapper.ExcelMapper;
import com.liyh.service.ExcelService;
import com.liyh.utils.ExcelTool;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @Author: liyh
 * @Date: 2020/10/23 17:44
 */

@Service
public class ExcelServiceImpl extends ServiceImpl<ExcelMapper, ProjectItem> implements ExcelService {

    private NumberFormat numberFormat = null;

    @Override
    public Result importProject(MultipartFile file) {
        // 解析Excel資料
        Result r = readDataFromExcel(file);

        List list = (List) r.getData();
        List<ProjectItem> items = list;

        if (items == null || items.size() <= 0) {
            return Result.error("沒有資料!!!");
        }

        //查詢之前是否存在專案清單項
        QueryWrapper wrapper = new QueryWrapper();
        wrapper.eq("is_deleted", 0);
        List<ProjectItem> beforeItems = baseMapper.selectList(wrapper);

        //如果存在,判斷兩個集合中是否有相同的專案序號
        if (beforeItems != null && beforeItems.size() > 0) {
            List<String> beforeOrderNumber = beforeItems.stream().map(ProjectItem::getOrderNumber).collect(Collectors.toList());
            List<String> afterOrderNumber = items.stream().map(ProjectItem::getOrderNumber).collect(Collectors.toList());

            for (String vo : beforeOrderNumber) {
                if (afterOrderNumber.contains(vo)) {
                    return Result.error(vo + ":該專案序號已經存在");
                }
            }
        }

        // 如果沒有序號相等,則插入資料表格中的資料,然後重新讀取
        for (ProjectItem item : items) {
            // 儲存資料
            int insert = baseMapper.insertProjectItem(item.getOrderNumber(), item.getName(), item.getContent(), item.getType(), item.getUnit(), item.getPrice(), item.getCount());
            if (insert <= 0) {
                return Result.error("匯入失敗");
            }
        }
        return Result.success("匯入成功");
    }

    /**
     * 解析Excel資料
     *
     * @param file 檔案
     * @return
     */
    public Result readDataFromExcel(MultipartFile file) {
        POIFSFileSystem pfs = null;
        Workbook workbook = null;
        try {
            // 解析xls和xlsx不相容問題
            workbook = ExcelTool.getWorkBook(pfs, workbook, file);
        } catch (IOException e) {
            e.printStackTrace();
            return Result.error("模板儲存異常。");
        }
        if (workbook == null) {
            return Result.error("請使用模板上傳檔案");
        }
        // 判斷有記錄的列數
        if (workbook.getSheetAt(0).getRow(0).getPhysicalNumberOfCells() != 7) {
            return Result.error("請使用型別所對應的模板");
        }

        numberFormat = NumberFormat.getNumberInstance();

        List<ProjectItem> list = new ArrayList<>();
        // 獲取表格第一個sheet的內容
        Sheet sheetAt = workbook.getSheetAt(0);
        // 獲得sheet總行數
        int lastRowNum = sheetAt.getLastRowNum();
        if (lastRowNum < 1) {
            return Result.error("資料錯誤");
        }
        // 開始讀取,不讀取表頭所以從第二行開始
        for (int i = 1; i <= lastRowNum; i++) {
            // 獲取每一行
            Row row = sheetAt.getRow(i);
            // 行為空不讀取
            if (row == null) continue;
            Cell cell = row.getCell(0);
            //列為空不讀取
            if (cell == null || StringUtils.isEmpty(convertData(cell))) continue;

            // 建立物件封裝行資料
            ProjectItem projectItem = new ProjectItem();
            // 建立一個集合根據下標來確定每個單元格對應物件的什麼屬性
            List<String> rowList = new ArrayList<>();
            //新增資料
            for (int j = 0; j < 7; j++) {
                Cell cellOne = row.getCell(j);
                try {
                    String item = convertData(cellOne);
                    rowList.add(item);
                } catch (Exception e) {
                    System.out.println("-------------------Err-----------------------");
                    System.out.println(i + "行" + j + "列資料轉換出現異常");
                    rowList.add("");
                }
            }
            //規避行數資料後幾行為空
            if (rowList.size() < 7) {
                for (int k = 0; k < 7 - rowList.size(); k++) {
                    rowList.add("");
                }
            }

            // 新增資料
            projectItem.setOrderNumber(rowList.get(0).trim());
            projectItem.setName(rowList.get(1).trim());
            projectItem.setContent(rowList.get(2).trim());
            if ("直接費".equals(rowList.get(3).trim())) {
                projectItem.setType(1);
            } else if ("間接費".equals(rowList.get(3).trim())) {
                projectItem.setType(2);
            } else if ("措施費".equals(rowList.get(3).trim())) {
                projectItem.setType(3);
            } else {
                projectItem.setType(null);
            }
            projectItem.setUnit(rowList.get(4).trim());
            projectItem.setPrice(rowList.get(5).trim());
            projectItem.setCount(rowList.get(6).trim());
            list.add(projectItem);
        }
        return Result.success("解析成功", list);
    }

    /**
     * 表格資料轉換
     *
     * @param cell
     * @return
     */
    public String convertData(Cell cell) {
        String str = "";

        switch (cell.getCellTypeEnum()) {
            case NUMERIC:
                //判斷是否是整數
                str = numberFormat.format(cell.getNumericCellValue());
                break;
            case STRING:
                str = cell.getStringCellValue();
                break;
            case _NONE:
                str = "";
                break;
            case BLANK:
                str = "";
                break;
            case FORMULA:
                try {
                    str = String.valueOf(cell.getNumericCellValue());
                } catch (IllegalArgumentException e) {
                    str = String.valueOf(cell.getRichStringCellValue());
                }
                break;
            default:
                str = "";
        }
        return str;
    }
}
ExcelServiceImpl

2.6 entity

package com.liyh.entity;

import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.io.Serializable;

/**
 * 專案清單表實體類
 *
 * @Author: liyh
 * @Date: 2020/10/23 17:05
 */
@Data
@TableName("project_item")
public class ProjectItem implements Serializable {

    private static final long serialVersionUID = 1L;

    /**
     * 主鍵uuid
     */
    private Integer id;
    /**
     * 專案序號
     */
    private String orderNumber;
    /**
     * 專案名稱
     */
    private String name;
    /**
     * 專案內容
     */
    private String content;
    /**
     * 費用型別(直接費等)
     */
    private Integer type;
    /**
     * 單位
     */
    private String unit;
    /**
     * 單價
     */
    private String price;
    /**
     * 數量
     */
    private String count;
    /**
     * 是否已刪除[0-否、1-是]
     */
    private String isDeleted;

}
ProjectItem

三、摸板檔案

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for project_item
-- ----------------------------
DROP TABLE IF EXISTS `project_item`;
CREATE TABLE `project_item`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `order_number` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL,
  `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL,
  `content` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL,
  `type` int(16) NULL DEFAULT NULL,
  `unit` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL,
  `price` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL,
  `count` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL,
  `is_deleted` int(1) UNSIGNED ZEROFILL NULL DEFAULT 0 COMMENT '是否已刪除[0-否、1-是]',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_bin ROW_FORMAT = Compact;

SET FOREIGN_KEY_CHECKS = 1;

四、啟動專案,進行測試

4.1 啟動專案,訪問結果:

4.2 測試檔案上傳

4.3 測試摸板下載

4.4 記得收藏哦^_^