【3】高并发导出场景下,服务器性能瓶颈优化方案-文件压缩

     使用EasyExcel导出并压缩文件是一种高效且常见的解决方案,尤其适用于需要处理大量数据的场景。

1. 导出多个Excel文件并压缩成ZIP文件的基本流程

 (1)数据准备:从数据库或其他数据源获取需要导出的数据,并将其存储在Java对象中(如List<YourEntity>)。

(2)创建Excel文件:使用EasyExcel库将数据写入多个Excel文件。每个Excel文件可以对应一个Sheet表单。

(3)压缩文件:将生成的Excel文件打包成ZIP格式,通常通过ZipOutputStream实现。

(4)响应输出:将压缩后的ZIP文件作为附件返回给客户端或上传到文件服务器(如腾讯云COS)。

2. 具体实现方法

(1)依赖引入

在项目的pom.xml中添加EasyExcel依赖:

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>easyexcel</artifactId>
    <version>4.0.2</version>
</dependency>

该版本支持多种功能,包括流式写入、分批处理等。

(2)生成Excel文件

定义一个基础类(如UserData),并使用EasyExcel写入数据:

import lombok.Data;

@Data
public class UserData {
    private Integer id;
    private String name;
    private Integer age;

    public UserData(Integer id, String name, Integer age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }
}

定义一个对象,存放Excel文件名和文件流,压缩时使用 

import lombok.Data;

import java.io.InputStream;

@Data
public class ZipDto {
    public String name;
    public InputStream inputStream;
    
    public ZipDto(String name, InputStream inputStream) {
        this.name = name;
        this.inputStream = inputStream;
    }
    
}

 生成excel文件

 public void exportUserData() {
        List<ZipDto> zipDtoList = new ArrayList<>();
        ZipDto zipDto = null;
        int pageNumber = 0;
        //每页查询数据库条数
        int PER_PAGE_SIZE =10000;
        com.alibaba.excel.ExcelWriter writer = null;
        WriteSheet sheet = null;
        try {
            while (true) {
                //分页从数据库加载业务数据
                List<UserData> list =baseMapper.findUserDataForExport(pageNumber * PER_PAGE_SIZE, PER_PAGE_SIZE);
                if (list.isEmpty()) {
                    break;
                }
                ByteArrayOutputStream oi = new ByteArrayOutputStream();
                writer = EasyExcel.write(oi, UserData.class).build();
                sheet = EasyExcel.writerSheet(0).build();
                writer.write(list, sheet);
                writer.finish();
                zipDto = new ZipDto("用户信息" + DateUtil.now() + "-" + pageNumber + ".xlsx", new ByteArrayInputStream(oi.toByteArray()));
                zipDtoList.add(zipDto);
                if (list.size() < PER_PAGE_SIZE) {
                    break;
                }
                pageNumber++;
            }
            if (CollUtil.isNotEmpty(zipDtoList)) {
                if (zipDtoList.size() == 1) {
                    zipDto = zipDtoList.get(0);
                    TencentCOSUtil.upLoad(zipDto.getInputstream(),
                    "userData_" + DateUtil.format(new Date(), "yyyy-MM"),"xlsx");
                } else {
                    ByteArrayOutputStream oi = new ByteArrayOutputStream();
                    //将多个excel压缩为Zip文件
                    ZipUtils.zipListStream(zipDtoList, oi);
                    //如果集群部署,会导致文件下载失败,上传文件到云存储
                    TencentCOSUtil.upLoad(new ByteArrayInputStream(oi.toByteArray()),
                            "userData_" + DateUtil.format(new Date(), "yyyy-MM"), "zip");
                }
            }
        } catch (Exception e) {
            log.error("[exportUserData]导出失败:{}", e);
        } finally {
            if (writer != null) {
                writer.close();
            }
        }
    }

   TencentCOSUtil.upload(InputStream inputStream, String path, String suffix) 是将文件上传到腾讯云文件服务器。后续单独写一篇介绍如何上传文件到腾讯云

(3)压缩为Zip文件
 /**
     * 将多个流转成zip文件输出
     *
     * @param listStream 文件流实体类对象
     * @param out        zip包的输出字节流
     * @return
     */
    public static boolean zipListStream(List<ZipDto> listStream, ByteArrayOutputStream out) {
        boolean flag = false;
        BufferedInputStream bis = null;
        try (ZipOutputStream zos = new ZipOutputStream(out)) {
            for (ZipDto zipDto : listStream) {
                String fileName = zipDto.getName();
                // 创建ZIP实体,并添加进压缩包
                ZipEntry zipEntry = new ZipEntry(fileName);
                zos.putNextEntry(zipEntry);
                // 读取待压缩的文件并写进压缩包里
                bis = new BufferedInputStream(zipDto.getInputStream());
                byte[] buffer = new byte[1024];
                int len = 0;
                while ((len = bis.read(buffer)) != -1) {
                    zos.write(buffer, 0, len);
                }
            }
            flag = true;
        } catch (Exception e) {
            log.error("[zipListStream]多个流转成zip文件异常",e);
        } finally {
            // 关闭流
            try {
                if (null != bis) {
                    bis.close();
                }
                if (null != out) {
                    out.close();
                }
            } catch (IOException e) {
                log.error(e.getMessage());
            }
        }
        return flag;
    }

3. 注意事项

  • 字符编码问题:在生成ZIP文件时,需确保文件名和内容的字符编码正确,避免中文乱码问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值