这是我参与更文挑战的第19天,活动详情查看: 更文挑战
前言
EasyExcel是生成Excel的一个利器,本文想解决的问题是当生成Excel时间过长,同步下载文件老超时的问题。 既然同步总是超时,那么我们就要将生成Excel的方法改为异步的,每当用户点击对应按钮时,发送mq给处理程序,将对应的数据放入Excel中后上传到OSS中,分成两步就是生成和分片上传。
使用到的包
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.2.10</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.7.2</version>
</dependency>
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>3.0.0</version>
</dependency>
hutool是用来操作文件的。
使用EasyExcel生成Excel
第一步中需要注意一点,我们要将Excel保存在本地临时文件中,因为如果直接流模式向OSS那边传递的话会大量的耗费内存 参见:github.com/alibaba/eas…
ExcelWriter excelWriter = null;
String fileName = "test.xlsx";
try {
//创建文件
FileUtil.touch(fileName);
excelWriter = EasyExcel.write(fileName, User.class).build();
WriteSheet writeSheet = EasyExcel.writerSheet("用户名单").build();
List<User> userList = new ArrayList<>();
userList.add(new User(1, "wlz"));
userList.add(new User(2, "lin"));
userList.add(new User(3, "shi"));
userList.add(new User(4, "ying"));
userList.add(new User(5, "di"));
excelWriter.write(userList, writeSheet);
} catch (Exception e) {
} finally {
if (excelWriter != null) {
excelWriter.finish();
}
}
分片上传OSS
上传时最好使用OSS的分片上传,帮助大文件进行快速上传。
InputStream inputStream = null;
OSS ossClient = null;
try {
File file = new File(fileName);
String fileAddress = "test.xlsx";
ossClient = new OSSClientBuilder().build("region", "secretId", "secretKey");
//创建InitiateMultipartuploadRequest对象。
InitiateMultipartUploadRequest request = new InitiateMultipartUploadRequest("bucketName", fileAddress);//初始化分片
InitiateMultipartUploadResult upresult = ossClient.initiateMultipartUpload(request);
//返回uploadId,它是分片上传事件的唯一标识,您可以根据这个ID来发起相关的操作,如取消分片上传、查询分片上传等。
String uploadId = upresult.getUploadId();
// partETags是PartETag的集合。PartETag由分片的ETag和分片号组成。
List<PartETag> partETags = new ArrayList<>();
//计算文件有多少个分片。
//单个分片大小2MB
long partSize = 2 * 1024 * 1024L;// "<localFile>”本地要上传的文件
long fileLength = file.length();
int partCount = (int) (fileLength / partSize);
if (fileLength % partSize != 0) {
partCount++;
}
//遍历分片上传。
for (int i = 0; i < partCount; i++) {
long startPos = i * partSize;
long curPartSize = (i + 1 == partCount) ? (fileLength - startPos) : partSize;
inputStream = new FileInputStream(fileName);
//跳过已经上传的分片。
inputStream.skip(startPos);
UploadPartRequest uploadPartRequest = new UploadPartRequest();
uploadPartRequest.setBucketName("bucketName");
uploadPartRequest.setKey(fileAddress);
uploadPartRequest.setUploadId(uploadId);
uploadPartRequest.setInputStream(inputStream);
//设置分片大小。除了最后一个分片没有大小限制,其他的分片最小为100KB。uploadPartRequest.setPartsize(curPartSize);
// 设置分片号。每一个上传的分片都有一个分片号,取值范围是1 ~10000,如果超出这个范围,oSS将返回InvalidArgument的错误码。
uploadPartRequest.setPartNumber(i + 1);
//每个分片不需要按顺序上传,甚至可以在不同客户端上传,oSS会按照分片号排序组成完整的文件。
UploadPartResult uploadPartResult = ossClient.uploadPart(uploadPartRequest);
//每次上传分片之后,0SS的返回结果会包含一个PartETag。PartETag将被保存到partETags中
partETags.add(uploadPartResult.getPartETag());
}
//创建CompleteMultipartUploadRequest对象。
//在执行完成分片上传操作时,需要提供所有有效的partETags。OSS收到提交的partETags后,会逐一验证每 个分片的有效性。当所有的数据分片验证通过后,OSS将 把这些分片组合成个完整的文 件。
CompleteMultipartUploadRequest completeMultipartUploadRequest = new CompleteMultipartUploadRequest("", fileAddress, uploadId, partETags);
// 完成上传。
CompleteMultipartUploadResult completeMultipartUploadResult = ossClient.completeMultipartUpload(completeMultipartUploadRequest);
} catch (Exception e) {
}
finally {
try {
if (ossClient != null) {
ossClient.shutdown();
}
if (inputStream != null) {
inputStream.close();
}
if(file!=null){
//使用Hutool的方法删掉文件
file.delete();
}
} catch (Exception e) {
}
}
总结
将大任务分成小点,各个突破,才是我们编程的不二法门。