【项目】优化百万数据导出 excel

文章描述了一个针对大数据量订单导出导致接口超时的问题,提出的解决方案是使用Job定时任务每10分钟执行一次,将数据导出到Excel并上传到腾讯云COS,同时设置文件2小时后失效。导出完成后,通过异步邮件通知用户下载链接。此方案有效解决了超时问题并实现了后台处理。
摘要由CSDN通过智能技术生成

需求

“全部导出订单”失败,原因在于数据量太大导致接口超时。

方案

因为是对导出时间不太敏感的业务场景,可以考虑使用该方案使用job进行大数据量的导出。具体方案如下图所示,其中job的时间为每10分钟执行一次。

在这里插入图片描述

实现

OrderInfoController

	/**
	 * @Description:   导出订单记录列表
	 */
	 private final FeignSysUserService feignSysUserService;
	private final UserExportTaskMapper userExportTaskMapper;
	
	@ApiOperation(value = "请求导出订单记录列表")
	@SysLog("请求导出订单记录列表")
	@PostMapping("/export")
	public void export() throws Exception {
		BaseUser user = SecurityUtils.getUser();
		if (ObjectUtil.isNull(user)) {
			throw new CheckedException("auto datascope, set up security details true");
		}
		R r = feignSysUserService.getUser(user.getId(),user.getType(),SecurityConstants.FROM_IN);
		if (!r.isOk()) {
			log.warn("全部订单下载通知的获取管理员信息远程服务查询失败!");
			throw new RuntimeException(r.getMsg());
		}
		//把object转换成json字符串
		String json = JSON.toJSONString(r.getData());
		//把json字符串转换成UserInfo对象
		UserVO userInfo = JSON.parseObject(json, UserVO.class);
		if (Objects.isNull(userInfo)) {
			log.warn("全部订单下载通知管理员信息不存在");
		}
		UserExportTask userExportTask = new UserExportTask();
		userExportTask.setId(userInfo.getId());
		userExportTask.setUsername(userInfo.getUsername());
		userExportTask.setNickName(userInfo.getNickName());
		userExportTask.setCreateTime(new Date());
		userExportTask.setEmail(userInfo.getEmail());
		userExportTask.setTenantId(userInfo.getTenantId());
		userExportTaskMapper.insert(userExportTask);

	}

写job:OrderInfoExportTask

/**
 * @Description: 商城订单导出任务
 */
@Slf4j
@Component("orderInfoExportTask")
@AllArgsConstructor
public class OrderInfoExportTask {

	private final OrderInfoService orderInfoService;

	private final CosConfigProperties cosConfigProperties;

	private final MailConfigProperties mailConfigProperties;


	private final UserExportTaskService userExportTaskService;
	/**
	 * @Description: 商城订单导出任务
	 */
	@XxlJob("orderInfoExportJobHandler")
	public ReturnT<String> orderInfoExportJob(String s) throws Exception {
		UserExportTask userExportTask = new UserExportTask();
		//查询所有的订单
		List<UserExportTask> userExportTaskList= userExportTaskService.list(Wrappers.query(userExportTask));
		if (userExportTaskList.size() > 0) {
			for (UserExportTask userExportTask1 : userExportTaskList) {
				//获取现在时间减去userExportTask1的时间
				long diffInMilliSec = System.currentTimeMillis() - userExportTask1.getCreateTime().getTime();
				long diffInMinutes = TimeUnit.MILLISECONDS.toMinutes(diffInMilliSec);
				//如果时间在10分钟以内,则执行
				if(diffInMinutes <=10){
					OrderInfo orderInfo = new OrderInfo();
					log.debug("orderInfoExportJob start...");
					TenantContextHolder.setTenantId(CommonConstants.USER_TYPE_1);
					List<OrderInfo> list = orderInfoService.selectOrderList(Wrappers.query(orderInfo));
					//讲数据写入到excel,并上传到cos上,生成地址
					String responseURL = excelLink(list,userExportTask1.getTenantId());
					if (StringUtils.isNotEmpty(responseURL)) {
						//异步邮件通知用户去下载
						notifyUser(responseURL,userExportTask1.getEmail(),userExportTask1.getUsername());
						log.info("orderInfoExportJob end...");
					}

				}

			}
		}
		return SUCCESS;
	}

	/**
	 * @Description: 讲数据写入到excel, 并上传到cos上, 生成地址
	 */
	private String excelLink(List<OrderInfo> list,String tenantId) throws Exception {
		String sheetName = "订单记录列表";
		FileItem fileItem;
		// 系统管理员类型用户,写入全部数据到excel,客服写入部分数据
		if (StrUtil.equals(CommonConstants.USER_TYPE_S, tenantId)) {
			List<OrderInfoExport> exportList = OrderInfoExportUtil.convertBean(list);
			ExcelUtil<OrderInfoExport> util = new ExcelUtil<>(OrderInfoExport.class);
			fileItem = util.exportExcelNew(exportList, sheetName);
		} else {
			List<OrderInfoExport> exportList = OrderInfoExportUtil.convertBean(list);
			List<OrderInfoExportCs> exportCsList = BeanUtil.copyToList(exportList, OrderInfoExportCs.class, null);
			ExcelUtil<OrderInfoExportCs> util = new ExcelUtil<>(OrderInfoExportCs.class);
			util.exportExcelNew(exportCsList, sheetName);
			fileItem = util.exportExcelNew(exportCsList, sheetName);
		}
		//存入cos,生成链接地址,并设置2小时后失效
		return uploadFile(fileItem, sheetName);
	}

	/**
	 * @Description: 上传文件到cos, 并设置2小时后失效
	 */
	private String uploadFile(FileItem fileItem, String dir) throws Exception {
		MultipartFile multipartFile = new CommonsMultipartFile(fileItem);
		File file = FileUtils.multipartFileToFile(multipartFile);
		SysConfigStorage sysConfigStorage = initSysConfigStorage();
		StorageConfig storageConfig = new StorageConfig();
		storageConfig.setAccessKeyId(sysConfigStorage.getAccessKeyId());
		storageConfig.setAccessKeySecret(sysConfigStorage.getAccessKeySecret());
		storageConfig.setEndpoint(sysConfigStorage.getEndpoint());
		storageConfig.setBucket(sysConfigStorage.getBucket());
		QcloudCosUtils qcloudCosUtils = new QcloudCosUtils(storageConfig);
		return qcloudCosUtils.uploadFileWithDeleteTime(file, dir);
	}

	/**
	 * @Description: 获取默认配置
	 */
	public SysConfigStorage initSysConfigStorage() {
		SysConfigStorage sysConfigStorage = new SysConfigStorage();
		sysConfigStorage.setStorageType(SysConfigStorage.StorageTypeEnum.QCLOUDCOS.getType());
		sysConfigStorage.setAccessKeyId(cosConfigProperties.getSecretId());
		sysConfigStorage.setAccessKeySecret(cosConfigProperties.getSecretKey());
		sysConfigStorage.setEndpoint(cosConfigProperties.getRegion());
		sysConfigStorage.setBucket(cosConfigProperties.getBucket());
		return sysConfigStorage;
	}

	/**
	 * @Description: 下载通知
	 */
	public void notifyUser(String responseURL ,String email,String realName) {
		DesvMailUtil mailUtil = new DesvMailUtil(mailConfigProperties.getUsername(), mailConfigProperties.getPassword(), mailConfigProperties.getDomain(), mailConfigProperties.getUri());
		HashMap<String, Object> params = new HashMap<String, Object>();
		String today = DateUtil.format(new Date(), DatePattern.CHINESE_DATE_PATTERN);
		params.put("to",email);
		params.put("username", "");
		params.put("title", "全部订单导出结果通知");
		String msgBody =
				"<p style=\"text-align:left;\">\r\n" +
						"	<br />\r\n" +
						"</p>\r\n" +
						"<p style=\"text-align:left;\">\r\n" +
						"	<span style=\"color:#333333;font-size:14px;\">To  {}:</span> \r\n" +
						"</p>\r\n" +
						"<p style=\"text-align:left;\">\r\n" +
						"	<span style=\"color:#333333;font-size:14px;\">全部订单导出地址为:{},请于两小时内下载。</span> \r\n" +
						"</p>\r\n" +
						"<p style=\"text-align:left;\">\r\n" +
						"	<span style=\"color:#333333;font-size:14px;\"> 两小时后链接将会失效哦,如失效,请再次发起导出需求。</span>\r\n" +
						"</p>\r\n" +
						"<p style=\"text-align:left;\">\r\n" +
						"	<span style=\"color:#333333;font-size:14px;\">{} </span> \r\n" +
						"<br />";
		msgBody = StrUtil.format(msgBody, realName, responseURL, today);
		params.put("item", msgBody);

		try {
			mailUtil.sendEmail(params);
		} catch (Exception e) {
			log.error("notifyUser error:{}", e.getMessage());
		}
		log.info("notifyUser {} sucesss",realName);

	}

}

其中uploadFile()方法:上传文件到cos, 并设置2小时后失效

/**
 * @JL 腾讯cos
 */
@AllArgsConstructor
@Slf4j
public class QcloudCosUtils {
	private final StorageConfig storageConfig;

	/**
	 * 上传文件(设置文件2小时后自动删除)
	 */

	public String uploadFileWithDeleteTime(File file, String dir) throws FileNotFoundException {
		// 1 初始化用户身份信息(secretId, secretKey)。
		String secretId = storageConfig.getAccessKeyId();
		String secretKey = storageConfig.getAccessKeySecret();
		COSCredentials cred = new BasicCOSCredentials(secretId, secretKey);
		// 2 设置 bucket 的区域, COS 地域的简称请参照 
		// clientConfig 中包含了设置 region, https(默认 http), 超时, 代理等 set 方法, 使用可参见源码或者常见问题 Java SDK 部分。
		Region region = new Region(storageConfig.getEndpoint());
		ClientConfig clientConfig = new ClientConfig(region);
		// 3 生成 cos 客户端。
		COSClient cosClient = new COSClient(cred, clientConfig);
		// 指定要上传到的存储桶
		String bucketName = storageConfig.getBucket();
		String fileName = file.getName();
		String suffix = fileName.substring(fileName.lastIndexOf(".") + 1);
		String key = dir + UUID.randomUUID() + "." + suffix;// 指定目标存储路径
		// 上传Excel文件到COS,设置过期时间为2小时
		ObjectMetadata metadata = new ObjectMetadata();
		Date expiration = new Date(System.currentTimeMillis() + 2 * 60 * 60 * 1000);
		metadata.setExpirationTime(expiration);
		InputStream inputStream = new FileInputStream(file);
		PutObjectRequest request = new PutObjectRequest(bucketName, key, inputStream, metadata);
		// 上传文件。
		cosClient.putObject(request);
		// 删除本地Excel文件
		if (!file.delete()) {
			log.error("Failed to delete file: " + file.getAbsolutePath());
		}
		// 关闭OSSClient。
		cosClient.shutdown();
		//链接两小时过期
		URL url = cosClient.generatePresignedUrl(bucketName, key, expiration);
		String resultStr = "https://" + url.getHost() + "/" + key;
		return resultStr;

	}
}

附录

百万数据导出 excel ,完美优化,性能爆表!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值