通用异步线程下载zip包导出excel文件
前言:页面点击下载按钮(批量导出excel文件,并且压缩成zip包格式导出),使用异步线程进行 查询数据、封装excel、导出下载 过程。
- 文件导出模板通用类
提取一个抽象类,泛型 继承于 导出参数模型基础类,以便于不同类型的文件导出,只需要改变入参类型(需要继承基础模型类)即可。生成通用抽象方法,生成入参、查询参数、生成文件、下载文件、导入外系统(DFS)等...
/**
* 文件导出模板通用类
*
* @author Hang.W
* @date 2019-08-26 16:18
*/
@Slf4j
public abstract class AbstractDownloadTemplate<T extends MctProdBaseReqDTO> {
/** dfs 工具类 */
@Autowired
private DfsFileDealUtils dfsFileDealUtils;
/** 下载中心 */
@Autowired
private MctProdReserveDownloadManager mctProdReserveDownloadManager;
/**
* 生成导出文件参数,并记录下载
*
* @param request
* @return
*/
public abstract MctCoreDownloadTaskInsertReqDTO insertRecord(T request);
/**
* 查询数据
*
* @param request
* @return
*/
public abstract Object queryDate(T request) throws Exception;
/**
* 生成文件
*
* @param o
* @return
*/
public abstract byte[] generateFile(T request, Object o, Object oj) throws Exception;
/**
* 下载方法
* @param request
* @param fileType
*/
public void download(T request, MctCoreDownloadTaskInsertReqDTO downloadTaskInsertReqDTO,
String fileType) {
DownLoadThread<T> downLoadThread = new DownLoadThread<>(this, request,
downloadTaskInsertReqDTO, fileType, null);
ThreadPoolUtils.execute(downLoadThread);
}
/**
* 下载方法
*
* @param request
* @param fileType
*/
public void download(T request, MctCoreDownloadTaskInsertReqDTO downloadTaskInsertReqDTO,
String fileType, Object o) {
DownLoadThread<T> downLoadThread = new DownLoadThread<>(this, request,
downloadTaskInsertReqDTO, fileType, o);
ThreadPoolUtils.execute(downLoadThread);
}
/**
* 完成文件上传外网
*
* @param t 请求数据
* @param fileFormat 文件格式
*/
public void finishDownload(T t, MctCoreDownloadTaskInsertReqDTO downloadTaskInsertReqDTO,
String fileFormat, Object o) {
try {
byte[] fileByte = generateFile(t, queryDate(t), o);
String filePath = dfsFileDealUtils.uploadFileByBytes(fileByte, fileFormat);
if (filePath != null) {
downloadTaskInsertReqDTO.setFileFullPath(MctProdConstant.DFS_HTTP_PATH + filePath);
downloadTaskInsertReqDTO
.setStatus(MctCoreFileDownloadTaskStatusEnum.SUCCESS.getCode());
downloadTaskInsertReqDTO.setUpdatedAt(new Date());
mctProdReserveDownloadManager.initOrUpdateDownloadTask(downloadTaskInsertReqDTO);
} else {
downloadTaskInsertReqDTO
.setStatus(MctCoreFileDownloadTaskStatusEnum.FAILURE.getCode());
downloadTaskInsertReqDTO.setUpdatedAt(new Date());
mctProdReserveDownloadManager.initOrUpdateDownloadTask(downloadTaskInsertReqDTO);
}
} catch (Exception e) {
log.info("文件上传外网异常,原因:{}", downloadTaskInsertReqDTO, e);
downloadTaskInsertReqDTO.setStatus(MctCoreFileDownloadTaskStatusEnum.FAILURE.getCode());
downloadTaskInsertReqDTO.setUpdatedAt(new Date());
mctProdReserveDownloadManager.initOrUpdateDownloadTask(downloadTaskInsertReqDTO);
}
}
}
- 具体文件下载类,继承于通用模板下载类
- 继承于上边通用模板下载类,重写生成入参、生成文件方法。
- 入参MctProdExportStoreReqDTO 门户导出类,泛型继承于 基础模型类。
- generateFile()生成excel文件方法中,POI创建HSSFWorkbook,excel,包括合并单元格,字体颜色等,最终生成导入zip包中
/**
* 门户信息导出Excel表格zip包格式
*
* @author Hang.W
* @date 2019-08-26 16:24
*/
@Slf4j
@Service
public class MctProdExportStoreInfoExcelProcess extends AbstractDownloadTemplate<MctProdExportStoreReqDTO> {
/** 下载中心表 */
@Autowired
private MctCoreDownloadTaskService mctCoreDownloadTaskService;
@Autowired
private SequenceUtils sequenceUtils;
@Autowired
private MctProdMerchantInfoManager mctProdMerchantInfoManager;
@Override
public MctCoreDownloadTaskInsertReqDTO insertRecord(MctProdExportStoreReqDTO reqDTO) {
MctCoreDownloadTaskInsertReqDTO downloadTaskInsertReqDTO = new MctCoreDownloadTaskInsertReqDTO();
downloadTaskInsertReqDTO.setMerchantCode(reqDTO.getMerchantCode());
downloadTaskInsertReqDTO.setFileId(sequenceUtils.getSequenceNo());
downloadTaskInsertReqDTO.setFileType(MctCoreFileDownloadTypeEnum.MERCHANT_STORE_INFO.getCode());
downloadTaskInsertReqDTO.setFileName(reqDTO.getMerchantCode() + "_商户门户信息" + ".zip");
downloadTaskInsertReqDTO.setFileFullPath("");
downloadTaskInsertReqDTO.setStatus(MctCoreFileDownloadTaskStatusEnum.DOING.getCode());
downloadTaskInsertReqDTO.setRemark("");
downloadTaskInsertReqDTO.setCreatedAt(new Date());
downloadTaskInsertReqDTO.setCreatedBy(reqDTO.getMerchantCode());
downloadTaskInsertReqDTO.setTraceLogId(reqDTO.getTraceLogId());
Result<Boolean> response = mctCoreDownloadTaskService.insertDownloadTask(downloadTaskInsertReqDTO);
if (response.isSuccess() && response.getResult()) {
return downloadTaskInsertReqDTO;
} else {
return null;
}
}
@Override
public Object queryDate(MctProdExportStoreReqDTO request) throws Exception {
return null;
}
@Override
public byte[] generateFile(MctProdExportStoreReqDTO mctProdExportStoreReqDTO, Object o, Object oj) throws Exception {
log.info("MctProdMerchantInfoServiceImpl.exportStore 门户信息导出Excel表格, 入参{}", mctProdExportStoreReqDTO);
Result<List<MctCoreStoreInfoResDTO>> result = mctProdMerchantInfoManager.exportStore(mctProdExportStoreReqDTO);
log.info("MctProdMerchantInfoServiceImpl.exportStore 门户信息导出Excel表格, 返回{}", result);
if(!result.isSuccess() || null == result.getResult()) {
log.error("MctProdMerchantInfoManager.exportStore 调用核心根据商户号merchantCode全量查询门户信息,失败:{}", result);
throw new MerchantProductBizException(BizErrorCode.EMPTY_QUERY_RESULT, result.getErrorMsg());
}
HSSFWorkbook workbook = new HSSFWorkbook();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
// 压缩输出流
ZipOutputStream zipOut = new ZipOutputStream(baos);
try {
HSSFSheet sheet = workbook.createSheet("门户信息");
sheet.setColumnWidth(0, 30 * 256);
sheet.setColumnWidth(1, 30 * 256);
sheet.setColumnWidth(2, 50 * 256);
sheet.setColumnWidth(3, 30 * 256);
sheet.setColumnWidth(4, 30 * 256);
sheet.setColumnWidth(5, 30 * 256);
sheet.setColumnWidth(6, 30 * 256);
sheet.setColumnWidth(7, 50 * 256);
sheet.setColumnWidth(8, 30 * 256);
sheet.setColumnWidth(9, 30 * 256);
// 设置字体颜色
HSSFFont font = workbook.createFont();
font.setColor(HSSFColor.RED.index);
HSSFCellStyle redFontStyle = workbook.createCellStyle();
redFontStyle.setFont(font);
HSSFRow row0 = sheet.createRow(0);
HSSFCell cell0 = row0.createCell(0);
cell0.setCellValue("填写说明(填写信息时请保留1-5行,删除第6行)");
cell0.setCellStyle(redFontStyle);
CellRangeAddress region0 = new CellRangeAddress(0, 0, 0, 9);
sheet.addMergedRegion(region0);
HSSFRow row1 = sheet.createRow(1);
row1.createCell(0).setCellValue("1、红色字体为必填项,请按照标准填写");
CellRangeAddress region1 = new CellRangeAddress(1, 1, 0, 9);
sheet.addMergedRegion(region1);
HSSFRow row2 = sheet.createRow(2);
row2.createCell(0).setCellValue("2、电话支持手机号和座机两种,只要输入一个即可,固定电话请加区号,如:010-12345678,手机号一般为11位数字");
CellRangeAddress region2 = new CellRangeAddress(2, 2, 0, 6);
sheet.addMergedRegion(region2);
HSSFRow row3 = sheet.createRow(3);
row3.createCell(0).setCellValue("3、同一商户门户名称/详细地址不可重复");
CellRangeAddress region3 = new CellRangeAddress(3, 3, 0, 1);
sheet.addMergedRegion(region3);
HSSFRow row = sheet.createRow(4);
HSSFCell cell4_0 = row.createCell(0);
cell4_0.setCellValue("门户名称");
cell4_0.setCellStyle(redFontStyle);
HSSFCell cell4_1 = row.createCell(1);
cell4_1.setCellValue("门户编号");
cell4_1.setCellStyle(redFontStyle);
row.createCell(2).setCellValue("分店名称");
HSSFCell cell4_3 = row.createCell(3);
cell4_3.setCellValue("门户属性");
cell4_3.setCellStyle(redFontStyle);
HSSFCell cell4_4 = row.createCell(4);
cell4_4.setCellValue("省份");
cell4_4.setCellStyle(redFontStyle);
HSSFCell cell4_5 = row.createCell(5);
cell4_5.setCellValue("城市");
cell4_5.setCellStyle(redFontStyle);
HSSFCell cell4_6 = row.createCell(6);
cell4_6.setCellValue("区县");
cell4_6.setCellStyle(redFontStyle);
HSSFCell cell4_7 = row.createCell(7);
cell4_7.setCellValue("详细地址");
cell4_7.setCellStyle(redFontStyle);
HSSFCell cell4_8 = row.createCell(8);
cell4_8.setCellValue("联系人");
cell4_8.setCellStyle(redFontStyle);
HSSFCell cell4_9 = row.createCell(9);
cell4_9.setCellValue("电话");
cell4_9.setCellStyle(redFontStyle);
int rowCount = 4;
for (MctCoreStoreInfoResDTO mctCoreStoreInfoResDTO : result.getResult()) {
rowCount++;
row = sheet.createRow(rowCount);
row.createCell(0).setCellValue(mctCoreStoreInfoResDTO.getStoreName());
row.createCell(1).setCellValue(mctCoreStoreInfoResDTO.getStoreNo());
row.createCell(2).setCellValue(mctCoreStoreInfoResDTO.getSubStoreName());
row.createCell(3).setCellValue(mctCoreStoreInfoResDTO.getStoreType());
row.createCell(4).setCellValue(mctCoreStoreInfoResDTO.getProvinceCode());
row.createCell(5).setCellValue(mctCoreStoreInfoResDTO.getCityCode());
row.createCell(6).setCellValue(mctCoreStoreInfoResDTO.getAreaCode());
row.createCell(7).setCellValue(mctCoreStoreInfoResDTO.getAddress());
row.createCell(8).setCellValue(mctCoreStoreInfoResDTO.getLinkMan());
row.createCell(9).setCellValue(mctCoreStoreInfoResDTO.getTelephone());
}
// 设置格式是文本
HSSFCellStyle style = workbook.createCellStyle();
style.setDataFormat(workbook.createDataFormat().getFormat("yyyy-mm-dd"));
sheet.setDefaultColumnStyle(0, style);
sheet.setDefaultColumnStyle(1, style);
sheet.setDefaultColumnStyle(2, style);
sheet.setDefaultColumnStyle(3, style);
sheet.setDefaultColumnStyle(4, style);
sheet.setDefaultColumnStyle(5, style);
sheet.setDefaultColumnStyle(6, style);
sheet.setDefaultColumnStyle(7, style);
sheet.setDefaultColumnStyle(8, style);
sheet.setDefaultColumnStyle(9, style);
log.info("全量导出门户信息zip包开始");
// 进行压缩存储
zipOut.setMethod(ZipOutputStream.DEFLATED);
// 压缩级别值为0-9共10个级别(值越大,表示压缩越厉害)
zipOut.setLevel(Deflater.BEST_COMPRESSION);
// 对需要压缩的文件命名
zipOut.putNextEntry(new ZipEntry("商户门户信息.xls"));
// 读取要压缩的字节输出流,进行压缩
workbook.write(zipOut);
zipOut.flush();
} catch (IOException e) {
log.error("全量导出门户信息,写入异常信息:{}, {}", e.getMessage(), e);
throw new RuntimeException(e);
} finally {
workbook.close();
baos.close();
zipOut.close();
}
log.info("全量导出门户信息zip包完成");
return baos.toByteArray();
}
}
- 进行文件导出步骤
- 创建interface接口,实现下载文件步骤
- 使用具体文件下载类,调用方法,生成下载文件入参,并且调用下载文件方法,开始下载文件
/**
* 异步文件导出类
*
* @author Hang.W
* @date 2019-08-26 16:37
*/
@Service
@Slf4j
public class MctProdMerchantInfoServiceImpl implements MctProdMerchantInfoService {
@Autowired
private MctProdExportStoreInfoExcelProcess mctProdExportStoreInfoExcelProcess;
/**
* 导出门户信息
*/
@Override
public Result<Boolean> exportStore(MctProdExportStoreReqDTO mctProdExportStoreReqDTO) {
MDC.put(Marker.TRACE_LOG_ID, mctProdExportStoreReqDTO.getTraceLogId());
Result<Boolean> result = new Result<>(Boolean.FALSE);
try {
log.info("MctProdMerchantInfoServiceImpl.exportStore 门户信息导出Excel表格, 入参{}", mctProdExportStoreReqDTO);
// 封装导出excel参数
MctCoreDownloadTaskInsertReqDTO mctCoreDownloadTaskInsertReqDTO = mctProdExportStoreInfoExcelProcess
.insertRecord(mctProdExportStoreReqDTO);
if (mctCoreDownloadTaskInsertReqDTO != null) {
// 执行导出excel操作
mctProdExportStoreInfoExcelProcess.download(mctProdExportStoreReqDTO,
mctCoreDownloadTaskInsertReqDTO, "zip");
result.setResult(Boolean.TRUE);
log.info("MctProdMerchantInfoServiceImpl.exportStore 门户信息导出Excel表格成功");
}
} catch (Exception e) {
log.error("MctProdMerchantInfoServiceImpl.exportStore 门户信息导出Excel表格, 异常{}", e);
result = DealExceptionUtil.doExceptionService(e);
}
return result;
}
}
- 异步线程开始下载文件
- 具体文件下载实现类中没有重写通用模板下载方法,所以使用通用模板download方法
- 在通用模板下载类中,创建线程,实现先上传,再下载的功能
- 线程类构造方法中入参传入,通用模板类,用于不同类型文件对象的下载
/**
* 异步线程下载
*
* @author Hang.W
* @date 2019-08-26 16:53
*/
public class DownLoadThread<T extends MctProdBaseReqDTO> implements Runnable {
private AbstractDownloadTemplate<T> abstractDownloadTemplate;
private MctCoreDownloadTaskInsertReqDTO downloadTaskInsertReqDTO;
private T request;
private Object o;
private String fileFormat;
public DownLoadThread(AbstractDownloadTemplate<T> abstractDownloadTemplate, T request,
MctCoreDownloadTaskInsertReqDTO downloadTaskInsertReqDTO,
String fileFormat, Object o) {
this.abstractDownloadTemplate = abstractDownloadTemplate;
this.request = request;
this.downloadTaskInsertReqDTO = downloadTaskInsertReqDTO;
this.fileFormat = fileFormat;
this.o = o;
}
@Override
public void run() {
abstractDownloadTemplate.finishDownload(request, downloadTaskInsertReqDTO, fileFormat, o);
}
}
- 具体门户模型类
/**
* 门户模型类
*
* @author Hang.W
* @date 2019-08-26 17:05
*/
@Getter
@Setter
@ToString
public class MctProdExportStoreReqDTO extends MctProdBaseReqDTO {
/** 门户号 */
@NotBlank(message = "门户号不能为空")
@NotNull(message = "门户号不能为空")
private String storeCode;
}
- 总结
- 提取通用模板文件下载类
- 针对不同的文件对象,例如:(用户信息,产品信息,资金信息等),不同的入参,可以使用同一种类的不同实现来进行功能
- 文中包括excel的生成,以及zip的下载