解决系统在大数据情况下如何导出文件(附代码)

解决系统在大数据情况下如何导出文件(附代码)_第1张图片

背景

系统的数据导出是一个重要的功能,而且对于excel类型的数据导出需求尤其多,如果系统的数据量不是很大,则无关紧要。但是系统的数据量如果非常巨大,对于导出数据来说就异常困难。

方法一

数据很少的情况下,进行数据导出,可以完全交给前端去做。前端人员可以通过接口获取的数据生成excel进行导出。数据量很少或者只允许用户每次导出很少的数据时,这种做法快速有效,成本很低,而且服务端只会承载数据查询的压力,考虑到数据库主从,这个压力就更小了。

同样,服务端也可以生成excel,通过流的形式进行输出,但是这种做法将压力交给了后端。

方法二(重点)

对于经常需要进行导出的系统来说,一般会搭建一个文件导出服务,这个服务专门为文件导出进行服务。

导出服务,对外提供导出的任务接口,不同业务只需要将需要导出的数据提交给这个服务,这个服务会完成一系列操作。

实现excel导出的步骤如下:

1.定义一个导出服务接口。该接口用于接收导出的数据。对于这个excel导出服务来说不应该处理数据,只需要接收即可。

 /**
     * 通用excel导出
     * 
     * @param list 需要导出的数据明细
     * @param model 数据对象(生成表头)
     * @param taskName 任务的名称
     * @param 
     */
    default  void export(List list, Class model, String taskName) {
        throw new UnsupportedOperationException();
    }

2.创建实现类。这里将整个流程划分成4步:

         a.创建导出任务。(该任务是为了在excel处理完成后,提供数据给前端进行展示,提供下载链接等信息。)

        b.生成excel文件。需要看下面注意事项

        c.上传至阿里云。(其它OSS也可,自己搭建的文件服务器也可)

       d.更新导出任务。

注意:在服务端生成excel是一个耗内存和磁盘的操作,所以我们需要尽可能的保证excel不能太大。这就意味着在接收数据的时候最好限制条目数,该限制方法可以在调用方处理,也可以在导出的内部处理。事例代码中没有显示处理这一问题。提供以下参考:使用了google的算法。

List partition = null;

if (!CollectionUtils.isEmpty(数据集合)) {

// 进行数据切割

partition = Lists.partition(数据集合, 5000);

}

完整代码如下:

public interface ExcelExportService {

/**

* 通用excel导出

*

* @param list 需要导出的数据明细

* @param model 数据对象

* @param taskName 任务的名称

* @param

*/

default void export(List list, Class model, String taskName) {

        throw new UnsupportedOperationException();

        }

}

@Service
@Slf4j
public class ExcelExportServiceImpl implements ExcelExportService {

    @Autowired
    private TaskMapper taskMapper;

    @Autowired
    private ExcelFactory excelFactory;

    @Resource
    private ThreadPoolTaskExecutor threadPool;

    @Override
    public  void export(List list, Class model, String taskName) {

        threadPool.execute(new Runnable() {
            @Override
            public void run() {
                // 1.创建导出任务
                Task task = createTask(taskName);

                // 2.生成excel文件
                ByteArrayOutputStream out = generateFile(list, model);

                // 3.上传至阿里云
                String key = uploadToOSS(out, taskName + ".xls");

                // 4.更新导出任务
                updateTask(task.getId(), key);
            }
        });
    }


    /**
     * @Author      xuhongchang
     * @Date        1/3/22  3:00 PM
     * @Describetion 创建导出任务
     */
    private Task createTask(String taskName) {
        Task task = new Task();
        task.setName(taskName);
        task.setState(TaskEnum.ING.getValue());
        task.setGmtCreated(new Date());
        task.setDownloadCount(0);
        taskMapper.insertSelective(task);
        return task;
    }

    /**
     * @Author      xuhongchang
     * @Date        1/3/22  3:30 PM
     * @Describetion 生成文件
     */
    public  ByteArrayOutputStream generateFile(List dataList, Class model) {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        try {
            excelFactory.createExportExcel().writeExcel(out, dataList, model);
        } catch (Exception e) {
            log.error("生成文件时报错:{}", e);
        }
        return out;
    }

    /***
     * @Author      xuhongchang
     * @Date        1/3/22  3:30 PM
     * @Describetion 上传文件
     */
    private String uploadToOSS(ByteArrayOutputStream out, String filename) {
        String endpoint = "XXXX";
        String accessKeyId = "XXXX";
        String accessKeySecret = "XXXX";
        String bucketName = "xhc-test-01";
        String dir = "fileCenter/";
        OSS ossClient = null;
        PutObjectResult result = null;
        String key = dir + filename;
        try {
            // 创建OSSClient实例。
            ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
            result = ossClient.putObject(bucketName, key, new ByteArrayInputStream(out.toByteArray()));
            log.info("上传返回结果: {}", result);
        } catch (OSSException e) {
            e.printStackTrace();
        } finally {
            // 关闭OSSClient。
            ossClient.shutdown();
            return result != null ? key : "";
        }
    }

    private void updateTask(Long id, String url) {
        Task task = new Task();
        task.setId(id);
        task.setState(StringUtils.isBlank(url) ? TaskEnum.FAIL.getValue() : TaskEnum.FINISHED.getValue() );
        task.setUrl(url);
        taskMapper.updateByPrimaryKeySelective(task);
    }

}
@Component
public class ExcelFactory {

    public ExportExcelUtil createExportExcel() {
        return new ExportExcelUtil<>();
    }
}
@Component
@Slf4j
public class ExportExcelUtil {

    public ExportExcelUtil() {
    }

    public void createExcel(ByteArrayOutputStream out, List data, List tableHeadList) throws IOException {
        try {
            List> head = getExcelHead(tableHeadList);

            ExcelWriter writer = new ExcelWriter(null, out, ExcelTypeEnum.XLSX, true);
            Table table = new Table(0);
            table.setHead(head);
            Sheet sheet1 = new Sheet(1, 0);
            sheet1.setAutoWidth(true);
            sheet1.setSheetName("sheet1");
            writer.write(data, sheet1, table);
            writer.finish();
            out.flush();
        } finally {
            if (out != null) {
                out.close();
            }
        }
    }

    private List> getExcelHead(List tableHeadList){
        List> head = new ArrayList>();
        for (String s : tableHeadList) {
            List column = new ArrayList();
            column.add(s);
            head.add(column);
        }
        return head;
    }

    public void writeExcel(ByteArrayOutputStream out, List data, Class model) throws Exception {
        WriteSheet writeSheet = new WriteSheet();
        writeSheet.setSheetName("sheet1");
        writeSheet.setSheetNo(1);
        EasyExcel.write(out, model).build().write(data, writeSheet).finish();
        out.flush();
    }

}
@Slf4j
@Configuration
public class ThreadPoolConfiguration {

    @Bean("threadPool")
    public ThreadPoolTaskExecutor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        // 设置核心线程数
        executor.setCorePoolSize(32);
        // 设置最大线程数
        executor.setMaxPoolSize(128);
        // 设置队列容量
        executor.setQueueCapacity(1000);
        // 设置线程活跃时间(秒)
        executor.setKeepAliveSeconds(60);
        // 设置默认线程名称
        executor.setThreadNamePrefix("业务线程-");
        // 设置拒绝策略
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        // 等待所有任务结束后再关闭线程池
        executor.setWaitForTasksToCompleteOnShutdown(true);
        return executor;
    }
}

导出的对象表头设置,导出的中model参数,可以这样设置

/**
 * 用户名称
 */
@ColumnWidth(35)
@ExcelProperty(value = "用户名称", index = 0)
private String userName;

/**
 * 年龄
 */
@ColumnWidth(35)
@ExcelProperty(value = "年龄", index = 1)
private Integer age;

/**
 * 地址
 */
@ColumnWidth(35)
@ExcelProperty(value = "地址", index = 2)
private String address;

嗯,结束啦!!!

你可能感兴趣的:(java,java)