欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

vue+SpringBoot的easyExcel导入导出

程序员文章站 2022-03-15 09:50:09
...

vue+SpringBoot的easyExcel导入导出

导入依赖:

<dependency>
  <groupId>org.apache.poi</groupId>
  <artifactId>poi-ooxml</artifactId>
  <version>4.0.1</version>
</dependency>
<dependency>
  <groupId>org.apache.poi</groupId>
  <artifactId>poi</artifactId>
   <version>4.0.1</version>
</dependency>
<dependency>
  <groupId>com.alibaba</groupId>
  <artifactId>easyexcel</artifactId>
  <version>2.1.6</version>
</dependency>

实体类及注解:

@Data
@AllArgsConstructor
@NoArgsConstructor
public class MonitorNoise {
    @ExcelIgnore
    private String id;

    @ExcelProperty(value = "设备名称",index = 0)
    private String name;

    @ExcelProperty(value = "时间",index = 1)
    private String time;

    @ExcelProperty(value = "设备位置",index = 2)
    private String place;
}

1.导入

controller层:

@RestController
@RequestMapping(value = "/import")
public class ImportController {

    @Autowired
    ImportNoiseListener noiseListener;
    
    //噪声监测数据导入
    @RequestMapping("/importNoise")
    public void importNoise(@RequestParam(name = "file",required = true) MultipartFile excl)
    {
        try {
            InputStream inputStream=excl.getInputStream();
            EasyExcel.read(inputStream, noiseListener)
                    // 设置sheet,默认读取第一个
                    .sheet()
                    .head(MonitorNoise.class)
                    // 设置标题所在行数
                    .headRowNumber(1)
                    .doReadSync();
        }catch (Exception e){
            e.printStackTrace();
        }
    }

监听器及调用service:

@Component
public class ImportNoiseListener extends AnalysisEventListener<MonitorNoise> {
    /**
     * 每隔5120条存储数据库,然后清理list ,方便内存回收
     */
    private static final int BATCH_COUNT = 128;
    private List<String> errMessage;

    List<MonitorNoise> noises = new ArrayList<>();
    private ImportService importService;

    public ImportNoiseListener(ImportService importService) {
        this.importService = importService;
        errMessage = new ArrayList<>();
    }

    @Override
    public void invoke(MonitorNoise monitorNoise, AnalysisContext context) {
        noises.add(monitorNoise);
        // 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
        if (noises.size() >= BATCH_COUNT) {
            errMessage.addAll(saveData());
            // 存储完成清理 list
            noises.clear();
        }
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        errMessage.addAll(saveData());
        noises.clear();
    }
    /**
     * 加上存储数据库
     */
    private List<String> saveData() {
        return importService.saveNoises(noises);
    }

    public List<String> getErrMessage() {
        return errMessage;
    }

    public void setErrMessage(List<String> errMessage) {
        this.errMessage = errMessage;
    }

}

service层接口:

@Service
public interface ImportService {
    List<String> saveNoises(List<MonitorNoise> list);
}

service层实现类:

@Service
public class ImportServiceImpl implements ImportService {

    @Autowired
    private NamedParameterJdbcTemplate namedParameterJdbcTemplate;

    @Override
    public List<String> saveNoises(List<MonitorNoise> noises) {
        ArrayList<String> errMessage = new ArrayList<>();
        MonitorNoise noise = new MonitorNoise();
        Map<String,Object> params = new HashMap<>();
        StringBuilder sql = new StringBuilder("INSERT INTO xxx (name, time, place) VALUES ( :name, :time, :place)");
        params.put("name", noise.getName());
        params.put("time", noise.getTime());
        params.put("place", noise.getPlace());
        SqlParameterSource[] paramss = SqlParameterSourceUtils.createBatch(noises.toArray());

        namedParameterJdbcTemplate.batchUpdate(sql.toString(), paramss);
        return errMessage;
    }
}

导出

controller层:

@RestController
@RequestMapping("/export")
public class ExportController {

    @Autowired
    ExportService exportService;

    @RequestMapping("/exportNoise")
    public void a(HttpServletResponse response){
        try{
            exportService.exportNoise(response);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

service层接口:

@Service
public interface ExportService {
    void exportNoise(HttpServletResponse response);
}

service层实现类:

@Service
public class ExportServiceImpl implements ExportService {

    @Autowired
    NamedParameterJdbcTemplate jdbcTemplate;

    @Override
    public void exportNoise(HttpServletResponse response) {
        try {
            List<MonitorNoise> list = new ArrayList<MonitorNoise>();
            String name = "噪声监测数据导入模板.xlsx";
            // 导出时候会出现中文无法识别问题,需要转码
            String fileName = new String(name.getBytes("gb2312"),"ISO8859-1");
            response.setContentType("application/vnd.ms-excel;chartset=utf-8");
            response.setHeader("Content-Disposition","attachment;filename=" + fileName);
            //调用工具类
            ExcelWriter writer = EasyExcel.write(response.getOutputStream()).registerWriteHandler(new CustomCellWriteHandler()).build();
            WriteSheet sheet = EasyExcel.writerSheet(0,"sheet").head(MonitorNoise.class).build();
            writer.write(list,sheet);
            writer.finish(); // 使用完毕之后关闭
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

涉及工具类:
导出excel自动列宽工具类:

/**
 * Excel 导出列宽度自适应
 */
public class CustomCellWriteHandler extends AbstractColumnWidthStyleStrategy {

    private Map<Integer, Map<Integer, Integer>> CACHE = new HashMap<>();

    @Override
    protected void setColumnWidth(WriteSheetHolder writeSheetHolder, List<CellData> cellDataList, Cell cell, Head head, Integer integer, Boolean isHead) {
        boolean needSetWidth = isHead || !CollectionUtils.isEmpty(cellDataList);
        if (needSetWidth) {
            Map<Integer, Integer> maxColumnWidthMap = CACHE.get(writeSheetHolder.getSheetNo());
            if (maxColumnWidthMap == null) {
                maxColumnWidthMap = new HashMap<>();
                CACHE.put(writeSheetHolder.getSheetNo(), maxColumnWidthMap);
            }

            Integer columnWidth = this.dataLength(cellDataList, cell, isHead);
            if (columnWidth >= 0) {
                if (columnWidth > 255) {
                    columnWidth = 255;
                }

                Integer maxColumnWidth = maxColumnWidthMap.get(cell.getColumnIndex());
                if (maxColumnWidth == null || columnWidth > maxColumnWidth) {
                    maxColumnWidthMap.put(cell.getColumnIndex(), columnWidth);
                    writeSheetHolder.getSheet().setColumnWidth(cell.getColumnIndex(), columnWidth * 256);
                }

            }
        }
    }

    private Integer dataLength(List<CellData> cellDataList, Cell cell, Boolean isHead) {
        if (isHead) {
            return cell.getStringCellValue().getBytes().length;
        } else {
            CellData cellData = cellDataList.get(0);
            CellDataTypeEnum type = cellData.getType();
            if (type == null) {
                return -1;
            } else {
                switch (type) {
                    case STRING:
                        return cellData.getStringValue().getBytes().length;
                    case BOOLEAN:
                        return cellData.getBooleanValue().toString().getBytes().length;
                    case NUMBER:
                        return cellData.getNumberValue().toString().getBytes().length;
                    default:
                        return -1;
                }
            }
        }
    }
}

vue前端导入时:

html代码:

	<el-dialog
      :title="title"
      :visible.sync="this.dialogVisibleNoise"
      width="600px"
      top="8vh"
      :before-close="colseDialog"
      :close-on-click-modal="false"
    >
      <el-row class="import-dialog-row">
        <el-col :span="12">
          <el-upload
            class="uploadFile"
            ref="upload"
            :action=""
            accept=".xls,.xlsx"
            async="false"
            :limit="1"
            :on-success="handleSuccess"
            :file-list="fileList"
            :before-upload="beforeUpload"
            :auto-upload="false"
            :on-remove="handleRemove"
            :on-progress="onProgress"
            element-loading-text="数据导入中,请稍等..."
            v-loading.fullscreen="loading"
          >
            <el-button slot="trigger" size="small" type="primary"
              >选择文件</el-button
            >
            <el-tooltip placement="right-start" style="padding-left: 20px">
              <div slot="content">单次导入,文件数据条数不能超过10000条</div>
              <i class="el-icon-warning"></i>
            </el-tooltip>
          </el-upload>
        </el-col>
      </el-row>
      <el-row style="line-height: 35px">
        <el-col :span="12">
          <div v-show="showInf">
            <span style="color: red">导入失败</span>
          </div>
        </el-col>
        <el-col :span="12"> </el-col>
      </el-row>
      <span slot="footer" class="dialog-footer">
        <el-button size="small" @click="handleClose">{{
          $t("base.button.cancel")
        }}</el-button>
        <el-button size="small" type="primary" @click="handleSave()"
          >导入</el-button
        >
      </span>
    </el-dialog>

js func代码:

	//取消按钮
    handleClose() {
      this.$refs.upload.clearFiles();
      this.isShow = true;
      this.showInf = false;
      this.dialogVisibleNoise = false;
      this.initNoiseData();
    },
    //导入按钮
    handleSave() {
      // debugger;
      let formFile = new FormData();
      formFile.append("file", this.$refs.upload.uploadFiles);
      // this.fileList = this.$refs.upload.uploadFiles;
      for (const key in this.fileList[0]) {
        // debugger;
        if (this.fileList[0].hasOwnProperty(key)) {
          if (this.fileList[0][key]) {
            formFile.append(key, this.fileList[0][key]);
          }
        }
      }
      this.$refs.upload.submit(formFile);
      this.isShow = false;
    },
    //上传成功事件
    handleSuccess(response) {
      var _this = this;
      _this.loading = false;
      console.log(response);
      _this.information = response;
      if (response.code === 500) {
        _this.showInf = true;
        _this.$message.error(response.message || "文件导入失败!");
        return;
      } else {
        _this.$message.success("文件导入成功!");
        _this.handleClose();
      }
    },
    colseDialog() {
      this.$refs.upload.clearFiles();
      this.isShow = true;
      this.showInf = false;
      this.dialogVisibleNoise = false;
    },
    //上传文件前,判断文件类型
    beforeUpload(file) {
      if (file.name.indexOf(".xls") == -1) {
        this.$message.error("只允许上传xls格式文件!");
        return false;
      }
      return true;
    },
    //文件移除事件
    handleRemove() {
      this.isShow = true;
      this.showInf = false;
    },
    onProgress() {
      this.loading = true;
    },

vue前端导出时:

html代码:

	<el-col :span="4">
          <el-button type="primary" size="small" @click="downloadTemplate">下载模板</el-button>
    </el-col>

js func代码:

//下载导入模板
    downloadTemplate(){
      var that = this;
      this.axios[""]
        .request({
          url: "",
          method: "get",
          responseType: "blob"
        })
        .then(function (response) {
          console.log(response)
          console.log(new Blob([response]));
          var blob = new Blob([response], { type: "application/x-xls" }); //创建一个blob对象
          var a = document.createElement("a"); //创建一个<a></a>标签
          a.href = URL.createObjectURL(blob); // response is a blob
          a.download = "000.xlsx"; //文件名称
          a.style.display = "none";
          document.body.appendChild(a);
          a.click();
          a.remove();
          that.dialogVisibleNoise = false
        })
        .catch(function (err) {
          console.log(err);
        });
    },

完毕