系统的数据导出是一个重要的功能,而且对于excel类型的数据导出需求尤其多,如果系统的数据量不是很大,则无关紧要。但是系统的数据量如果非常巨大,对于导出数据来说就异常困难。
方法一数据很少的情况下,进行数据导出,可以完全交给前端去做。前端人员可以通过接口获取的数据生成excel进行导出。数据量很少或者只允许用户每次导出很少的数据时,这种做法快速有效,成本很低,而且服务端只会承载数据查询的压力,考虑到数据库主从,这个压力就更小了。
同样,服务端也可以生成excel,通过流的形式进行输出,但是这种做法将压力交给了后端。
方法二(重点)对于经常需要进行导出的系统来说,一般会搭建一个文件导出服务,这个服务专门为文件导出进行服务。
导出服务,对外提供导出的任务接口,不同业务只需要将需要导出的数据提交给这个服务,这个服务会完成一系列操作。
实现excel导出的步骤如下:1.定义一个导出服务接口。该接口用于接收导出的数据。对于这个excel导出服务来说不应该处理数据,只需要接收即可。
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 {
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);
}
});
}
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;
}
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;
}
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;
嗯,结束啦!!!



