专栏名称: 沉默王二
技术文通俗易懂,吹水文风趣幽默。学 Java,认准二哥的网站 javabetter.cn
目录
相关文章推荐
51好读  ›  专栏  ›  沉默王二

被阿里抛弃的那个项目,救活了!

沉默王二  · 公众号  · 后端  · 2024-12-20 14:04

主要观点总结

本文主要介绍了作者如何将EasyExcel-Plus更名为FastExcel,并介绍了使用FastExcel实现Excel文件导出的前后端代码实现过程,包括接口设计、数据模型设计、Axios的使用、自定义图标的添加以及对服务端返回数据的处理等。同时,还提到了使用Apifox进行接口测试以及模拟500万条数据导出Excel的相关内容。

关键观点总结

关键观点1: FastExcel的简介和更名背景

作者介绍了EasyExcel-Plus更名为FastExcel的缘由,并强调了开源精神的重要性。

关键观点2: 后端代码实现

详细描述了后端代码实现的步骤,包括引入FastExcel依赖、添加接口、设置响应类型和数据处理等。

关键观点3: 前端代码实现

介绍了前端代码的封装过程,包括Axios的get请求、响应类型的设置、自定义图标的添加以及对服务端返回数据的处理等。

关键观点4: 使用Apifox进行接口测试

介绍了如何在开发过程中使用Apifox插件进行接口测试,提高开发效率。

关键观点5: 模拟大数据量导出Excel的挑战和解决方案

作者提到了模拟500万条数据导出Excel的挑战,并给出了相应的解决方案,包括如何插入批量数据、保证MySQL连接不断开、使用CountDownLatch和自定义线程池写入Excel等。


正文

你好,我是二哥呀,EasyExcel-Plus 正式更名为 FastExcel,并且作者正在发力推广。那作为一名同样喜欢开源的爱好者,我必须得为他们的开源精神做一点点微薄的贡献,那就是通知一声你(😄。

https://github.com/CodePhiliaX/fastexcel

要知道,FastExcel 的前身 EasyExcel 在 GitHub 上有多达 32.9k star,深受开发者的喜爱。

只不过在今年早些时候,作者从阿里离职了,阿里也因此官宣 EasyExcel 走到了生命的尽头,毕竟维护开源项目需要付出巨大的精力和时间。

看了一眼 EasyExcel 的提交记录,多达 1196 次,不容易啊。

刚好技术派中缺少导出 Excel 这一趴,于是我就索性亲自上阵把它实现了。

代码我已经提交到了 GitHub,Spring Boot 后端和 React 都有。你可以拉取最新的 main 分支查看,网上也有很多类似的例子,但要不只有前端,要不只有后端,没有完整的 web 示例。对于新手或者小白来说,还是不知道该怎么使用。

入口放在了技术派 admin 端的首页, PVPU 这块刚好有一个 echarts,可以下载近 7 天、30 天、90 天、180 天的历史数据。

完成这个小功能,简历上就可以写上这么一条(详细参考技术派教程):

https://paicoding.com/column/6/16

你看,简历含金量是不是瞬间就提起来了。

实现起来也非常简单。

01、后端代码实现

第一步,在需要 FastExcel 的 module 中引入依赖

<dependency>
    <groupId>cn.idev.excelgroupId>
    <artifactId>fastexcelartifactId>
    <version>${fastexcel.version}version>
dependency>

按照关键字在技术派的源码中搜就可以找得到了。

第二步,在处理下载请求的 controller 中添加接口,注意看 HttpServletResponse 返回对象都设置了哪些属性。

@GetMapping("pvUvDayDownload2Excel")
public void pvUvDayDownload2Excel(@RequestParam(name = "day", required = false) Integer day,
                                  HttpServletResponse response) throws IOException 
{
    response.reset();
    response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
    response.setCharacterEncoding("utf-8");
    String fileName = URLEncoder.encode("技术派""UTF-8").replaceAll("\\+""%20");
    response.setHeader("Content-disposition""attachment;filename*=utf-8''" + fileName + ".xlsx");

    // 获取数据
    day = (day == null || day == 0) ? DEFAULT_DAY : day;
    statisticsSettingService.download2Excel(day, response);
}

①、response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"):设置响应的内容类型为 Excel 文件的 MIME 类型。

②、response.setCharacterEncoding("utf-8");:设置响应的字符编码为 UTF-8。

③、String fileName = URLEncoder.encode("技术派", "UTF-8").replaceAll("\\+", "%20");:对文件名进行 URL 编码,并将加号替换为 %20,以确保文件名在 URL 中正确显示。

④、response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");:设置响应头,指示浏览器以附件形式下载文件,并指定文件名。

第三步,调用 FastExcel 的 API 实现 Excel 文件的写入

public void download2Excel(Integer day, HttpServletResponse response) {
    List pvDayList = requestCountService.getPvUvDayList(day);
    // StatisticsDayDTO 转 StatisticsDayExcelDTO
    List excelDTOList = StatisticsConverter.convertToExcelDTOList(pvDayList);

    // TODO 这里可以用一个大文件,比如说 500万条数据测试一下,看看 FastExcel 的性能
    try {
        FastExcel.write(response.getOutputStream(), StatisticsDayExcelDTO.class)
                .sheet(day + "天统计")
                .doWrite(excelDTOList)
;
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}

FastExcel.write 方法接受输出流和数据类型,sheet 方法设置工作表名称,doWrite 方法将数据写入工作表。

注意这里的 StatisticsDayExcelDTO 为数据类型,也就是 Excel 中的列。只需要使用注解 @ExcelProperty 指定一下列名就好了。

@Data
public class StatisticsDayExcelDTO {
    @ExcelProperty("日期")
    private String date;

    @ExcelProperty("PV")
    private Long pvCount;

    @ExcelProperty("UV")
    private Long uvCount;
}

到此为止,后端的工作就完成了,接下来,我们来看前端的代码怎么写。

02、前端代码实现

第一步,先封装 Axios 的 get 请求

get(url: string, config: AxiosRequestConfig = {}): Promise> {
    console.log("开始执行 get 请求", url, config);
    return this.service.get(url, config);
}

Axios 是一个基于 promise(一个由异步函数返回的对象)的网络请求库,封装了 XMLHttpRequests,可以将将请求体序列化为JSON、FormData、x-www-form-urlencoded,同时兼容 Blob 格式。

get 方法接受两个参数:url 和 config。url 是一个字符串,表示请求的目标地址;config 是一个可选的配置对象,默认值为空对象,用于配置请求的详细信息。

函数的返回类型是一个 Promise,其中包含了 ResultData 类型的数据。T 是一个泛型参数。

this.service 是一个 Axios 实例,这里做了一些封装,感兴趣的话,你可以去看 paicoding-admin 的源码。

第二步,为 get 请求设置 blob 类型的响应类型

export const download2ExcelPvUvApi = (day: number) => {
 return http.get(`${PORT1}/statistics/pvUvDayDownload2Excel?day=${day}`, { responseType: "blob" });
};

①、export 导出 download2ExcelPvUvApi 函数,以便在其他模块中使用。

②、URL 中有一个请求参数 day。

③、{ responseType: "blob" },指定响应类型为 blob(Binary Large Object,通常用于处理文件下载,例如图像、视频、PDF 文件等),用于处理二进制数据。

第三步,在 echarts 的 toolbar 中添加一个下载 Excel 的小图标

feature: {
    myDownloadExcel: {
    show: true,
    title: "下载 Excel",
    icon: "path://M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-1 14h-3v-3h-2v3H8v-3H6v3H5v-2h3v-2H5V5h14v9h-2v3z", // 自定义图标
    onclick: exportToExcel, // 点击按钮触发导出函数
    },
}

第四步,处理服务端返回的数据

// 导出数据为 Excel 文件
const exportToExcel = async () => {
  // 通过 dayLimitList 获取对应的天数
      const day = dayLimitList.find(item => item.value === pvUvDay)?.value;
      if (!day) return;

      console.log("导出的天数是", day);

      // 调用下载接口
      const response = await download2ExcelPvUvApi(Number(day));

      const contentDisposition = response.headers["content-disposition"];
      let fileName = "paicoding.xlsx";

      if (contentDisposition) {
          const matches = contentDisposition.match(/filename\*?=utf-8''([^;]+)/i);
          if (matches && matches.length === 2) {
              fileName = decodeURIComponent(matches[1]);
          }
      }
      console.log("文件名是", fileName);

      // 将返回的 Blob 数据转换为可下载文件
      const blob = new Blob([response.data], {
          type: response.headers["content-type"],
      });
      console.log("Blob 数据大小:", blob);

      const url = window.URL.createObjectURL(blob);
      const link = document.createElement("a");
      link.href = url;
      link.download = fileName; // 下载的文件名
      link.click();

      // 释放 Blob URL,避免内存泄漏
      window.URL.revokeObjectURL(url);

};

①、exportToExcel 为异步函数,await 的作用是暂停函数的执行,直到 await 后面的 Promise 对象完成,并返回该 Promise 的解析值。

②、从响应头中获取 content-disposition,并尝试解析出文件名。如果解析失败,则使用默认文件名 paicoding.xlsx。

③、将响应数据转换为 Blob 对象,并设置其 MIME 类型。

④、创建一个临时的 URL 对象,并创建一个 元素。设置其 href 属性为 Blob 对象的 URL,download 属性为文件名,然后模拟点击该链接以触发下载。

⑤、释放之前创建的 Blob URL,避免内存泄漏。

到此为止,整个 Excel 的前后端请求就全部搞定了,是不是很简单?

03、Apifox 进行接口测试

现在的开发都讲究工程化,前后端分离,所以技术派也是前后端分离的。

对于后端开发者来说,完成开发工作后可以先自测一下,然后就扔一个 API 文档给前端开发者,就算完成工作了。

如果 IntelliJ IDEA 中安装了 Apifox 插件,还可以将后端的 controller 接口上传到 Apifox 中,甚至直接在 IntelliJ IDEA 中唤起进行测试。

非常方便。

04、模拟 500 万条数据导出Excel

这部分内容我放到了技术派上,你可以通过下面这个专栏地址去查看,也算是交给你的一个作业。

https://paicoding.com/column/6/20

包括怎么插入批量插入 500 万条数据,怎么保证 MySQL 连接不断开,怎么使用 CountDownLatch + 自定义线程池去写入 Excel 等。

这些源码我都会提交到 GitHub,你需要的就是动动手去查看一下。

加油。

ending

一个人可以走得很快,但一群人才能走得更远。二哥的编程星球已经有 6700 多名球友加入了,如果你也需要一个优质的学习环境,戳链接 🔗 加入我们吧。你可以阅读星球专栏、向二哥提问、帮你制定学习计划、精修简历、和球友一起打卡成长(舒服了)。

两个置顶帖「球友必看」和「知识图谱」里已经沉淀了非常多优质的学习资源,相信能帮助你走的更快、更稳、更远。

如果觉得有帮助,不妨随手点个赞、在看、转发三连吧,如果你想第一时间收到推送,也可以给我加个星标 🌟 谢谢你看我的文章,我们明天见。

最后,把二哥的座右铭送给大家:没有什么使我停留——除了目的,纵然岸旁有玫瑰、有绿荫、有宁静的港湾,我是不系之舟。共勉 💪。