Bootstrap

vue+element-plus上传Excel到java springboot后台,并且按行读取

最近业务遇到的需求:
需求1. 前端上传excel文件,后端接收文件并读取处理,最后返回处理完成的数据;
需求2. 再次和其他参数一块儿上传,后端完成处理,并生成excel文件并发送邮件到客户留的邮箱。

本文只完成了需求1;
需求2:java springboot使用企业微信公共邮箱发带附件Excel文件的邮件

1. 正常使用element-plus的el-upload

1.1 html

<el-upload
	drag
	:limit="limitNum"
	:auto-upload="false"
	:action="UploadUrl()"
	:before-upload="beforeUploadFile"
	:on-change="fileChange"
	:on-exceed="exceedFile"
	:on-success="handleSuccess"
	:on-error="handleError"
	:file-list="fileList"
	>
    <el-icon class="el-icon--upload"><upload-filled /></el-icon>
    <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
    <template #tip>只能上传xlsx文件,且不超过10M</template>
</el-upload>
<el-button size="small" type="primary" @click="uploadFile">立即上传</el-button>
<el-button size="small">取消</el-button>

1.2 ts

methods: {
    //上传from表单
    async submitForm() {
      if (this.formInline.excelData.length == 0) {
        return ElMessage({
          message: "请先按照格式上传文件!",
          type: "error",
          duration: 3 * 1000,
        });
      } else if (
        this.formInline.companyAddress == "" ||
        this.formInline.companyName == "" ||
        this.formInline.companyEmail == ""
      ) {
        return ElMessage({
          message: "请填写公司信息",
          type: "error",
          duration: 3 * 1000,
        });
      }
      //判断邮箱和公司名称

      const res = await post("/scheme/upload-data", this.formInline);

      if (res.state) {
        console.log(res, res.data);
      }
    },
    
    // 文件超出个数限制时的钩子
    exceedFile(files, fileList) {
      ElMessage({
        message: `只能选择 ${this.limitNum} 个文件,当前共选择了 ${
          files.length + fileList.length
        }`,
        type: "warning",
        duration: 5 * 1000,
      });
    },

    // 文件状态改变时的钩子
    fileChange(file) {
      console.log(file.raw);
      let extension = file.name.split(".")[1];
      let size = file.size / 1024 / 1024;
      if (extension !== "csv" && extension !== "xlsx" && extension !== "xls") {
        return ElMessage.warning("只能上传后缀是csv/xlsx/xls的文件");
      }
      console.log(size);
      if (size > 10) {
        console.log(this.fileList);
        return ElMessage.warning("文件大小不得超过10M");
      } else {
        this.fileList.push(file.raw);
      }
    },

    // 文件上传成功时的钩子
    handleSuccess(res, fileList) {
      ElMessage({
        message: "文件上传成功,请填写下面参数",
        type: "success",
        duration: 3 * 1000,
      });
    },
    // 文件上传失败时的钩子
    handleError(err, fileList) {
      ElMessage({
        message: "文件上传失败,请按照格式重新上传文件!",
        type: "error",
        duration: 3 * 1000,
      });
    },
    UploadUrl() {
      // 因为action参数是必填项,我们使用二次确认进行文件上传时,直接填上传文件的url会因为没有参数导致api报404,
      // 所以这里将action设置为一个返回为空的方法就行,避免抛错
      return "";
    },
    async uploadFile() {
      if (this.fileList.length === 0) {
        ElMessage({
          message: "请上传文件",
          type: "warning",
          duration: 3 * 1000,
        });
      } else {
        let form = new FormData();
        form.append("file", this.fileList[0]);
        console.log(form.get("file"));
        await upload({
          method: "post",
          url: "/scheme/upload",
          headers: {
            "Content-type": "multipart/form-data",
          },
          data: form,
        }).then(
          (res) => {
            if (res.state && res.data.length > 0) {
              this.handleSuccess(res, this.fileList);
              this.formInline.excelData = [];
              this.formInline.excelData = res.data;
            } else {
              this.handleError(res, this.fileList);
              this.fileList = [];
            }
          },
          (err) => {
            this.handleError(res, this.fileList);
            this.fileList = [];
          }
        );
      }
    },
  },

1.3 api.ts

import axios from 'axios'
import { ElMessage, ElMessageBox } from 'element-plus'

// const getToken=()=>{
//   let url = window.location.href
//   let urlStr = url.split('?')[1].split("=")[1];
//   return urlStr;
// }
const DEV_BASE_API = "http://localhost:8088";
// const PRO_BASE_API = "";


// create an axios instance
const service = axios.create({
  baseURL: DEV_BASE_API, // api 的 base_url
  timeout: 60000, // request timeout
  withCredentials: true
})

// request interceptor
service.interceptors.request.use(
  config => {
    // Do something before request is sent
    config.headers['tenantId'] = "10001"
    // 让每个请求携带token-- ['X-Token']为自定义key 请根据实际情况自行修改
    config.headers['uni-token'] = ""
    config.headers['x-client-type'] = ''
    config.withCredentials = true
    return config
  },
  error => {
    // Do something with request error
    console.warn(error) // for debug
    Promise.reject(error)
  }
)


// response interceptor
service.interceptors.response.use(
  /**
   * 下面的注释为通过在response里,自定义code来标示请求状态
   * 当code返回如下情况则说明权限有问题,登出并返回到登录页
   * 如想通过 xmlhttprequest 来状态码标识 逻辑可写在下面error中
   * 以下代码均为样例,请结合自生需求加以修改,若不需要,则可删除
   */
  response => {
    const res = response.data
    if (response.status == 200 && response.config.responseType == 'blob') {
      return response
    }

    if (res.code !== 200 || response.status != 200) {
      ElMessage({
        message: res.message,
        type: 'error',
        duration: 5 * 1000
      })
      // 50008:非法的token; 50012:其他客户端登录了;  50014:Token 过期了;
      if (res.code === 1001 || res.code === 1002 || res.code === 1003 || res.code === 1000) {
        // 请自行在引入 MessageBox
        // import { Message, MessageBox } from 'element-ui'
        ElMessageBox.confirm('你已被登出,可以取消继续留在该页面,或者重新登录', '确定登出', {
          confirmButtonText: '重新登录',
          cancelButtonText: '取消',
          type: 'warning'
        }).then(() => {
          store.dispatch('LogOut').then(() => {
            location.reload() // 为了重新实例化vue-router对象 避免bug
          })
        })
      }
      return Promise.reject('error')
    } else {
      return response.data
    }
  },
  error => {
    console.log(error)
    ElMessage({
      message: '服务端异常',
      type: 'error',
      duration: 5 * 1000
    })
    return Promise.reject(error)
  }
)

export const post =(url, data) =>{
  return service({
    url: url,
    method: 'post',
    data: {
      ...data
    }
  })
}

export const get = (url) =>{
  return service({
    url: url,
    method: 'get'
  })
}

export const upload = (file)=>{
  return service(file)
}



1.4 前端效果

在这里插入图片描述

2. java (springboot)

2.1 api


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
// 请原谅我把和公司项目目录的包名删除;

import java.io.*;
import java.lang.reflect.Array;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

@RestController
@RequestMapping(value = "/scheme", name = "上传方案Excel")
public class EmsUploadExcelApi {

    private final EmsExcelService excelService;

    public EmsUploadExcelApi(EmsExcelService excleService) {
        this.excelService = excleService;
    }

    @ResponseBody
    @PostMapping(value = "/upload", name = "上传方案Excel")
    public Object upload(@RequestParam("file") MultipartFile multiFile) {
        if (multiFile != null) {
            List result = null;

            File file = null;
            try {
                file = MultipartFileToFile.multipartFileToFile(multiFile);
                ExcelUtil<EmsUploadExcel> excelUtil = new ExcelUtil<>();
                List<EmsUploadExcel> models = excelUtil.excelReadForFile(file, null, EmsUploadExcel.class);
                // 处理文件
                result = this.excelGetPower(models);
                //最后删除掉 因为multipartFile-->File而产生的 存在项目根路径下的 缓存文件
                MultipartFileToFile.delteTempFile(file);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                File f = new File(file.toURI());
                f.delete();
            }
            return R.ok().setData(result);
        } else {
            return R.fail("文件上传错误");
        }
    }

    // 处理excel
    public List excelGetPower(List<EmsUploadExcel> emsExcel) throws ParseException {
        List result = new ArrayList<>();

        for (int i = 0; i < emsExcel.size(); i++) {
            String date = emsExcel.get(i).getDate();
            if (i == emsExcel.size() - 1) {
                if (date == null || "".equals(date + ""))
                    continue;
            }
            // 判断空值排除
            if (emsExcel.get(i).getPower() == null)
                continue;

            Double a;
            a = emsExcel.get(i).getPower();
            JSONObject o = new JSONObject();
            o.put("rate", a * emsExcel.get(i).getMultiple());
            o.put("date", emsExcel.get(i).getDate());
            result.add(o);
        }
        result = filterExcelData(result);
        return result;
    }

    /*
     * 过滤数据
     *
     * @param list
     *
     * */
    public List filterExcelData(List list) throws ParseException {
        List<ArrayList> result = new ArrayList<>(4);
		// 过滤数据部分省略
        return result;
    }

    /**
     * 通过秒毫秒数判断两个时间的间隔的秒数
     * @param date1
     * @param date2
     * @return
     */
    public static boolean differentDaysByMillisecond(Date date1,Date date2,int len)
    {
        boolean times =  Math.abs(date2.getTime() - date1.getTime()) < (len / 4) * 3600*1000 ;
        return times;
    }
}

2.2 读文件 excelUtil

参考: 真忘记了,CSDN一个大牛写的;如果谁知道请告诉我
import au.com.bytecode.opencsv.CSVReader;
import com.alibaba.excel.EasyExcel;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.web.multipart.MultipartFile;

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

@Slf4j
public class ExcelUtil<T> {
    public static final String CSV_SUFFIX = ".csv";
    public static final String XLS_SUFFIX = ".xls";
    public static final String XLSX_SUFFIX = ".xlsx";

    /**
     * 转换特定类型的集合
     *
     * @param file
     * @param requestId
     * @param t
     * @return
     */
    @SuppressWarnings("unchecked")
    public List<T> excelRead(MultipartFile file, String requestId, T t) {
        List<JSONObject> jsonObjects = transExcelForMultipartFile(file, requestId);
        String objs = JSON.toJSONString(jsonObjects);
        return (List<T>) JSONObject.parseArray(objs, t.getClass());
    }

    /**
     * 转换特定类型的集合
     *
     * @param file
     * @param requestId
     * @param clazz
     * @return
     */
    @SuppressWarnings("unchecked")
    public List<T> excelReadForFile(File file, String requestId, Class<T> clazz) {
        List<JSONObject> jsonObjects = transExcelForFile(file, requestId);
        String objs = JSON.toJSONString(jsonObjects);
        List a = JSONObject.parseArray(objs,clazz);
        return a;
    }

    /**
     * 读取上传Excel文件
     *
     * @param file
     * @param requestId
     * @return
     */
    private static List<JSONObject> transExcelForMultipartFile(MultipartFile file, String requestId) {
        List<JSONObject> resultList = null;
        // 获取文件名
        String fileName = file.getOriginalFilename();
        // 获取文件后缀名,截取.后面的类容
        assert fileName != null;
        String suffix = fileName.substring(fileName.lastIndexOf("."));
        try (InputStream inputStream = file.getInputStream()) {
            resultList = transExcel(requestId, inputStream, suffix);
        } catch (IOException e) {
            log.error("【黑名单导入】导入文件解析异常,requestId:{},message:{}", requestId, e.getMessage());
            e.printStackTrace();
        }
        return resultList;
    }

    /**
     * 读取Excel文件
     *
     * @param file
     * @param requestId
     * @return
     */
    private static List<JSONObject> transExcelForFile(File file, String requestId) {
        List<JSONObject> resultList = null;
        // 获取文件名
        String fileName = file.getName();
        // 获取文件后缀名,截取.后面的类容
        String suffix = fileName.substring(fileName.lastIndexOf("."));
        try (InputStream inputStream = new FileInputStream(file)) {
            resultList = transExcel(requestId, inputStream, suffix);
        } catch (IOException e) {
            log.error("【黑名单导入】导入文件解析异常,requestId:{},message:{}", requestId, e.getMessage());
        }
        return resultList;
    }

    /**
     * 根据文件后缀名区分读取Excel表格方法
     *
     * @param requestId
     * @param inputStream
     * @param suffix
     * @return
     */
    private static List<JSONObject> transExcel(String requestId, InputStream inputStream, String suffix) {
        List<JSONObject> resultList = null;
        if (XLS_SUFFIX.equals(suffix)) {
            // .xls文件
            resultList = readXlsFile(requestId, inputStream);
        } else if (XLSX_SUFFIX.equals(suffix)) {
            // .xlsx文件
            resultList = readXlsxFile(requestId, inputStream);
        } else if (CSV_SUFFIX.equals(suffix)) {
            // .csv文件
            resultList = readCsvFile(requestId, inputStream);
        } else {
            // 不支持格式,抛出异常
        }
        return resultList;
    }

    /**
     * 读取xls文件
     *
     * @param requestId
     * @param in
     * @return
     */
    private static List<JSONObject> readXlsFile(String requestId, InputStream in) {
        List<JSONObject> resultList = new ArrayList<>();
        HSSFWorkbook hssfWorkbook;
        try {
            hssfWorkbook = new HSSFWorkbook(in);
            // 获取Excel中所有的sheet
            // int sheetNum = hssfWorkbook.getNumberOfSheets();
            // 获取导入之前Excel文档中的选中页
            int activeSheetIndex = hssfWorkbook.getActiveSheetIndex();
            // 获取选中页中的表格数据
            HSSFSheet hssfSheet = hssfWorkbook.getSheetAt(activeSheetIndex);
            //获取该页有多少行数据
            int rowNum = hssfSheet.getLastRowNum();
            //处理列名数据、自动转化表格标题名为列名
            List<WorkBookTitleVO> WorkBookTitleVOS = analysisWorkBookForXls(hssfSheet);
            for (int i = 1; i <= rowNum; i++) {
                // 获取第i行数据
                HSSFRow hssfRow = hssfSheet.getRow(i);
                JSONObject obj = new JSONObject();
                int finalI = i;
                WorkBookTitleVOS.forEach(vo -> {
                    // 将所有字段都默认以字符串的格式读取
//                    hssfRow.getCell(vo.getCellIndex()).setCellType(CellType.STRING);
//                    obj.put(vo.getColumnName(), hssfRow.getCell(vo.getCellIndex()).toString());
                    try {
                        hssfRow.getCell(vo.getCellIndex()).setCellType(CellType.STRING);
                        obj.put(vo.getColumnName(), hssfRow.getCell(vo.getCellIndex()).toString());
                    } catch (Exception e) {
                        log.info("第" + finalI + "行");
                        e.printStackTrace();
                    }
                });
                resultList.add(obj);
            }
        } catch (IOException e) {
            log.error("Xls文件解析异常,requestId:{},message:{}", requestId, e.getMessage());
        }
        return resultList;
    }

    /**
     * 读取xlsx文件
     *
     * @param requestId
     * @param in
     * @return
     */
    private static List<JSONObject> readXlsxFile(String requestId, InputStream in) {
        List<JSONObject> resultList = new ArrayList<>();
        XSSFWorkbook xssfWorkbook = null;
        try {
            //将InputStream转XLSX对象(即表格对象)
            xssfWorkbook = new XSSFWorkbook(in);
            // 获取导入之前Excel文档中的选中页
            int activeSheetIndex = xssfWorkbook.getActiveSheetIndex();
            // 获取选中页中的表格数据
            XSSFSheet xssfSheet = xssfWorkbook.getSheetAt(activeSheetIndex);
            //获取该页有多少行数据
            int rowNum = xssfSheet.getLastRowNum();
            //处理列名数据、自动转化表格标题名为列名
            List<WorkBookTitleVO> WorkBookTitleVOS = analysisWorkBookForXlsx(xssfSheet);
            //遍历列表中的每一行数据,并将之封装为JSONObject对象
            for (int i = 1; i <= rowNum; i++) {
                // 获取第i行数据
                XSSFRow xssfRow = xssfSheet.getRow(i);
                JSONObject objx = new JSONObject();
                int finalI = i;
                WorkBookTitleVOS.forEach(vo -> {
                    // 将所有字段都默认以字符串的格式读取
                    try {
                        xssfRow.getCell(vo.getCellIndex()).setCellType(CellType.STRING);
                        objx.put(vo.getColumnName(), xssfRow.getCell(vo.getCellIndex()).toString());
                    } catch (Exception e) {
                        log.info("第" + finalI + "行无数据");
                        e.printStackTrace();
                    }
                });
                resultList.add(objx);
            }
        } catch (IOException e) {
            log.error("Xlsx文件解析异常,requestId:{},message:{}", requestId, e.getMessage());
        }
        return resultList;
    }

    /**
     * 读取csv文件
     *
     * @param requestId
     * @param in
     * @return
     */
    private static List<JSONObject> readCsvFile(String requestId, InputStream in) {
        List<JSONObject> resultList = new ArrayList<>();
        InputStreamReader reader = new InputStreamReader(in);
        CSVReader csvReader = new CSVReader(reader);
        List<String[]> list = null;
        try {
            //将InputStream转XLSX对象(即表格对象)
            list = csvReader.readAll();
            //处理列名数据、自动转化表格标题名为列名
            List<WorkBookTitleVO> WorkBookTitleVOS = analysisWorkBookForCsv(list);
            //遍历列表中的每一行数据,并将之封装为JSONObject对象
            for (int i = 1; i < list.size(); i++) {
                // 获取第i行数据
                String[] csvRow = list.get(i);
                JSONObject obj = new JSONObject();
                WorkBookTitleVOS.forEach(vo -> {
                    obj.put(vo.getColumnName(), csvRow[vo.getCellIndex()]);
                });
                resultList.add(obj);
            }
        } catch (IOException e) {
            log.error("Xlsx文件解析异常,requestId:{},message:{}", requestId, e.getMessage());
        }
        return resultList;
    }

    /**
     * 分析xls文件选中页表格标题栏
     *
     * @return
     */
    private static List<WorkBookTitleVO> analysisWorkBookForXls(HSSFSheet hssfSheet) {
        // 获取该页有多少列数据
        int cellNum = hssfSheet.getRow(0).getPhysicalNumberOfCells();
        // 遍历取出每一列的第一行数据作为标题存入集合中
        List<WorkBookTitleVO> titleList = new ArrayList<>();
        for (int i = 0; i < cellNum; i++) {
            // 取出第一行数据
            HSSFRow hssfRow = hssfSheet.getRow(0);
            // 存入表格title实体类中
            WorkBookTitleVO vo = new WorkBookTitleVO();
            vo.setCellIndex(i);
            vo.setColumnName(hssfRow.getCell(i).toString() + "");
            titleList.add(vo);
        }
        return titleList;
    }

    /**
     * 分析xlsx文件选中页表格标题栏
     *
     * @return
     */
    private static List<WorkBookTitleVO> analysisWorkBookForXlsx(XSSFSheet xssfSheet) {
        // 获取该页有多少列数据
        int cellNum = xssfSheet.getRow(0).getPhysicalNumberOfCells();
        // 遍历取出每一列的第一行数据作为标题存入集合中
        List<WorkBookTitleVO> titleList = new ArrayList<>();
        for (int i = 0; i < cellNum; i++) {
            // 取出第一行数据
            XSSFRow xssfRow = xssfSheet.getRow(0);
            // 存入表格title实体类中
            WorkBookTitleVO vo = new WorkBookTitleVO();
            vo.setCellIndex(i);
            vo.setColumnName(xssfRow.getCell(i).toString());
            titleList.add(vo);
        }
        return titleList;
    }

    /**
     * 分析csv文件选中页表格标题栏
     *
     * @return
     */
    private static List<WorkBookTitleVO> analysisWorkBookForCsv(List<String[]> list) {
        // 获取该页有多少列数据
        int cellNum = list.get(0).length;
        // 遍历取出每一列的第一行数据作为标题存入集合中
        List<WorkBookTitleVO> titleList = new ArrayList<>();
        for (int i = 0; i < cellNum; i++) {
            // 取出第一行数据
            String[] row = list.get(0);
            // 存入表格title实体类中
            WorkBookTitleVO vo = new WorkBookTitleVO();
            vo.setCellIndex(i);
            vo.setColumnName(row[i]);
            titleList.add(vo);
        }
        return titleList;
    }

    /**
     * 生成excel文件
     * @param fileName excel文件路径
     * @param dataList 数据列表
     * @param clazz 导出对象类
     * @param <T>
     * @return
     * @throws IOException
     */
    public static <T> File generateExcel(String fileName, List<T> dataList, Class<T> clazz) throws IOException {
        // 生成文件
        File excel = new File(fileName);
        // excel写入
        EasyExcel.write(excel,clazz).sheet(0).doWrite(dataList);
        return excel;
    }
}

2.3 EmsUploadExcel (上传的文件第一行标题必须是date,power,multiple)

import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;

import java.io.Serializable;

@Getter
@Setter
@EqualsAndHashCode
@Data
public class EmsUploadExcel implements Serializable{
    @ExcelProperty("日期")
    private String date;

    @ExcelProperty("瞬时有功")
    private Double power;

    @ExcelProperty("倍率")
    private Integer multiple;
}

如有错误,请指正!

最后如果帮到你了,点个赞

;