SpringBoot+Vue+ElementUI实现导入和导出

1.导出数据

1.1.后端需要导入的maven依赖

我用的是原生的poi,没有使用easyExcel和hutool,因为是作为学习使用,了解原生代码

 <!-- excel工具 -->
            <dependency>
                <groupId>org.apache.poi</groupId>
                <artifactId>poi-ooxml</artifactId>
                <version>4.1.2</version>
            </dependency>

            <!--            poi导出excel-->
            <dependency>
                <groupId>org.apache.poi</groupId>
                <artifactId>poi</artifactId>
                <version>4.1.2</version>
            </dependency>

1.2编写controller

  • 因为是在浏览器下载,所以是通过response.getOutputStream响应回去的
  • 我这里没有使用poi的工具类,实际项目开发中会使用工具类简化代码开发的,不用害怕

最主要的是,有时候我们有些业务需要导出表格:

  • 比如我需要导出所有的数据,这个时候直接导出,都不需要通过SQL过滤
  • 但是如果用户先通过搜索再导出数据呢?所以需要一个表单来控制,当有用户搜索后,点击导出按钮,我们可以将前端搜索框中的表单数据一并通过导出按钮事件传给后端
/**
     * 导出操作日志excel导出操作日志excel
     * @param pageQuerySysLogOperationModel
     * @param response
     * @throws IOException
     */
    @ApiOperation("导出操作日志excel")
    @PostMapping("/export")
    public void export(@RequestBody @Valid PageQuerySysLogOperationModel pageQuerySysLogOperationModel, HttpServletResponse response) throws IOException {
    
    
        List<SysLogOperation> list = sysLogOperationService.selectSysLogOperationList(pageQuerySysLogOperationModel);
        // 新建一个工作表
        XSSFWorkbook workbook = new XSSFWorkbook();
        // 新建一个表单
        XSSFSheet sheet = workbook.createSheet("操作日志");
        // 第一行,也是标题行
        XSSFRow rowTitle = sheet.createRow(0);
        rowTitle.createCell(0).setCellValue("操作序号");
        rowTitle.createCell(1).setCellValue("操作描述");
        rowTitle.createCell(2).setCellValue("请求地址");
        rowTitle.createCell(3).setCellValue("请求方式");
        rowTitle.createCell(4).setCellValue("请求参数");
        rowTitle.createCell(5).setCellValue("请求时长");
        rowTitle.createCell(6).setCellValue("操作地址");
        rowTitle.createCell(7).setCellValue("操作人员");
        rowTitle.createCell(8).setCellValue("状态");
        rowTitle.createCell(9).setCellValue("操作时间");
        // 数据从第一行开始
        int rowNum = 1;
        for (SysLogOperation sysLogOperation : list) {
    
    
            Row row = sheet.createRow(rowNum);
            row.createCell(0).setCellValue(sysLogOperation.getId());
            row.createCell(1).setCellValue(sysLogOperation.getOperation());
            row.createCell(2).setCellValue(sysLogOperation.getRequestUri());
            row.createCell(3).setCellValue(sysLogOperation.getRequestMethod());
            row.createCell(4).setCellValue(sysLogOperation.getRequestParams());
            row.createCell(5).setCellValue(sysLogOperation.getRequestTime());
            row.createCell(6).setCellValue(sysLogOperation.getIp());
            row.createCell(7).setCellValue(sysLogOperation.getUsername());
            row.createCell(8).setCellValue(sysLogOperation.getStatus() == 0 ? "成功":"失败");
            row.createCell(9).setCellValue(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(Date.from(sysLogOperation.getCreateTime().atZone(ZoneId.systemDefault()).toInstant())));
            rowNum++;
        }
        String fileName = "操作日志_"+ String.valueOf(System.currentTimeMillis()) +".xlsx";
        response.setCharacterEncoding("UTF-8");
        response.setHeader("Access-Control-Expose-Headers","Content-Disposition");
        response.setHeader("Content-Disposition", "attachment;filename="+ URLEncoder.encode(fileName,"UTF-8"));
        response.setHeader("Content-Type", "application/vnd.ms-excel");
        response.setContentType("application/vnd.ms-excel;charset=utf-8");
        response.setCharacterEncoding("UTF-8");
        OutputStream outputStream = response.getOutputStream();
        workbook.write(outputStream);
        outputStream.flush();
        outputStream.close();
        workbook.close();
    }

浏览器下载最主要的一部分代码

String fileName = "操作日志_"+ String.valueOf(System.currentTimeMillis()) +".xlsx";
response.setCharacterEncoding("UTF-8");
response.setHeader("Access-Control-Expose-Headers","Content-Disposition");
response.setHeader("Content-Disposition", "attachment;filename="+ URLEncoder.encode(fileName,"UTF-8"));
response.setHeader("Content-Type", "application/vnd.ms-excel");
response.setContentType("application/vnd.ms-excel;charset=utf-8");
response.setCharacterEncoding("UTF-8");
OutputStream outputStream = response.getOutputStream();
workbook.write(outputStream);
outputStream.flush();

1.3 前端代码

因为前端代码太多了,只整些核心关键代码

<el-button type="primary" @click="handleExport"
        >导出<i class="el-icon-top"></i
      ></el-button>

<script>
export default {
      
      
	methods:{
      
      
		handleExport() {
      
      
		   this.request
		     .post("/manager/sys/operation/export", this.queryForm, {
      
      
		       responseType: "blob",
		     })
		     .then((res) => {
      
      
		       if (!res.data) {
      
      
		         return;
		       }
		       const blob = new Blob([res.data], {
      
      
		         type: "application/vnd.ms-excel",
		       }); // 构造一个blob对象来处理数据,并设置文件类型
		       const href = URL.createObjectURL(blob); //创建新的URL表示指定的blob对象
		       const a = document.createElement("a"); //创建a标签
		       a.style.display = "none";
		       a.href = href; // 指定下载链接
		       let fileName = res.headers["content-disposition"];
		       fileName = fileName.split("=")[1];
		       a.download = decodeURIComponent(fileName); //指定下载文件名
		       a.click(); //触发下载
		       URL.revokeObjectURL(a.href); //释放URL对象
		     });
		 },
	}
}
</script>

2. 导入数据

2.1 编写后端代码

难点在于excel中数据的格式,在转换中可能会报错

public R<String> importData(@RequestParam("file") MultipartFile multipartFile) throws IOException, ParseException {
    
    
        // 获取io输入流
        InputStream inputStream = multipartFile.getInputStream();
        // 将输入流读取到workbook中
        Workbook workbook = new XSSFWorkbook(inputStream);
        for (Sheet sheet : workbook) {
    
    
            int lastRowNum = sheet.getLastRowNum();
            List<SysLogOperation> sysLogOperations = new ArrayList<>();
            for (int i = 1; i <= lastRowNum; i++) {
    
    
                SysLogOperation sysLogOperation = new SysLogOperation();
                Row row = sheet.getRow(i);
                String operation = row.getCell(1).getStringCellValue();
                sysLogOperation.setOperation(operation);
                String requestURI = row.getCell(2).getStringCellValue();
                sysLogOperation.setRequestUri(requestURI);
                String requestMethod = row.getCell(3).getStringCellValue();
                sysLogOperation.setRequestMethod(requestMethod);
                String requestParams = row.getCell(4).getStringCellValue();
                sysLogOperation.setRequestParams(requestParams);
                double requestTime = row.getCell(5).getNumericCellValue();
                sysLogOperation.setRequestTime((int) requestTime);
                String ip = row.getCell(6).getStringCellValue();
                sysLogOperation.setIp(ip);
                String username = row.getCell(7).getStringCellValue();
                sysLogOperation.setUsername(username);
                String status = row.getCell(8).toString();
                sysLogOperation.setStatus(status.equals("成功")?(short)0:(short)1);
                String createTime = row.getCell(9).getStringCellValue();
                sysLogOperation.setCreateTime(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(createTime).toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime());
                sysLogOperations.add(sysLogOperation);
            }
            Assert.isTrue(sysLogOperationService.saveBatch(sysLogOperations),"添加失败");
        }
        return R.ok();
    }

2.2 前端代码

<el-upload
        class="upload-demo"
        action="http://localhost:8080/manager/sys/operation/import"
        :on-success="handleUploadSuccess"
        accep=".xlsx"
        :show-file-list="false"
        :before-upload="beforeUpload"
        style="display: inline-block; margin-right: 10px"
      >
        <el-button type="primary">导入<i class="el-icon-bottom"></i></el-button>
      </el-upload>

<script>
export default {
      
      
	methods:{
      
      
		beforeUpload(file) {
      
      
	      const isLimit = file.size / 1024 / 1024 < 10;
	
	      if (file.type.indexOf("application/vnd") === -1) {
      
      
	        this.$message.error("上传的文件格式不对吧,亲!");
	        return false;
	      }
	
	      if (!isLimit) {
      
      
	        this.$message.error("上传的文件不能大于10M!");
	        return false;
	      }
	    },
	    handleUploadSuccess(res, file, fileList) {
      
      
	      let formData = new FormData();
	      formData.append("file", file);
	      this.$message.success(res.msg);
	    },
	  },
	}
}

</script>

这里有一些配置需要结合ElementUI官方文档

  • accept 接受上传的文件类型(thumbnail-mode 模式下此参数无效)
  • on-success 文件上传成功时的钩子,这里一定要将文件传输过去,不然后端接收不到数据
  • before-upload 上传文件之前的钩子,参数为上传的文件,若返回 false 或者返回 Promise 且被 reject,则停止上传。可以进行文件大小的判断,和文件格式是否正确的判断
  • show-file-list 是否显示已上传文件列表
  • action 必选参数,上传的地址

3.遇到的一些细节问题

3.1 时间日期格式的转换

因为我在项目中的日期类型是LocalDateTime类型所以他的值是这样的:2022-09-13T17:09:07
每次中间会有一个‘T’,所以我们可以使用@JsonFormat(pattern = “yyyy-MM-dd HH:mm:ss”)把他的格式转换一下
然后就是时间格式的问题可以使用SimpleDateFormat(“这里写格式”)

3.2 LocalDateTime和Date的相互转换

  • LocalDateTime、Date 在数据库中对应的类型 datetime
  • Date 和 SimpleDateFormat 都是线程不安全的
  • LocalDateTime 和 DateTimeFormatter 都是线程安全的

LocalDateTime 转为Date类型

public static Date localDateTimeToDate(LocalDateTime localDateTime) {
    
    
	ZoneId zoneId = ZoneId.systemDefault();
	ZonedDateTime zdt = localDateTime.atZone(zoneId);
	Date date = Date.from(zdt.toInstant());
	return date;
}

Date类型转换为LocalDateTime


public static LocalDateTime dateToLocalDateTime(Date date) {
    
    
	Instant instant = date.toInstant();
	ZoneId zoneId = ZoneId.systemDefault();
	LocalDateTime localDateTime = instant.atZone(zoneId).toLocalDateTime();
	return localDateTime;
}

3.3 response响应回去的中文数据乱码

在进行文件通过浏览器下载的时候,后端传给前端一个文件名,文件名是中文的,会出现乱码,是如何解决的呢?
后端代码

String fileName = "操作日志_"+ String.valueOf(System.currentTimeMillis()) +".xlsx";
response.setHeader("Content-Disposition", "attachment;filename="+URLEncoder.encode(fileName,"UTF-8"));     

前端代码:

handleExport() {
    
    
      this.request
        .post("/manager/sys/operation/export", this.queryForm, {
    
    
          responseType: "blob",
        })
        .then((res) => {
    
    
          if (!res.data) {
    
    
            return;
          }
          const blob = new Blob([res.data], {
    
    
            type: "application/vnd.ms-excel",
          }); // 构造一个blob对象来处理数据,并设置文件类型
          const href = URL.createObjectURL(blob); //创建新的URL表示指定的blob对象
          const a = document.createElement("a"); //创建a标签
          a.style.display = "none";
          a.href = href; // 指定下载链接
          let fileName = res.headers["content-disposition"];
          fileName = fileName.split("=")[1];
          a.download = decodeURIComponent(fileName); //指定下载文件名
          a.click(); //触发下载
          URL.revokeObjectURL(a.href); //释放URL对象
        });
    },

后端通过: URLEncoder.encode(fileName,“UTF-8”);
前端通过: decodeURIComponent(fileName);

猜你喜欢

转载自blog.csdn.net/weixin_46073538/article/details/127085859