上篇将大文件上传到文件服务器,本篇在上篇基础上结合阿里云api实现将大文件上传大阿里云OSS。
阿里云分片示例地址:阿里云使用分片上传的方式上传大文件_对象存储(OSS)-阿里云帮助中心
上传流程
- 将待上传文件按照一定大小进行分片。
- 使用InitiateMultipartUpload接口初始化一个分片上传任务。
- 使用UploadPart接口上传分片。文件切分成Part之后,文件顺序是通过上传过程中指定的partNumber来确定,所以您可以并发上传这些碎片。并发数并非越多越快,请结合自身网络状况和设备负载综合考虑。如果您希望终止上传任务,可调用AbortMultipartUpload接口,成功上传的Part会一并删除。
- 使用CompleteMultipartUpload接口将Part组合成一个Object。
说明:其中初始化分片上传任务会生成一个uploadId,它是分片上传事件的唯一标识,上传每个分片都需要这个标识,可以根据这个ID来发起相关的操作,如取消分片上传、查询分片上传等。
在上篇基础上实现将文件分片上传到阿里云OSS:
提供OSS工具类:
@Component
public class OSSUtil {
private static Logger logger = LoggerFactory.getLogger(OSSUtil.class);
@Value("${oss.endpoint.ext}")
private String endpoint;
@Value("${oss.accessKeyId}")
private String accessKeyId ;
@Value("${oss.accessKeySecret}")
private String accessKeySecret ;
@Value("${oss.bucketName}")
private String bucketName ;
private OSS ossClient;
@PostConstruct
public void init() {
ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
}
/**
* 分块上传完成获取结果
*/
public String completePartUploadFile(String fileKey, String uploadId, List<PartETag> partETags) {
CompleteMultipartUploadRequest request = new CompleteMultipartUploadRequest(bucketName, fileKey, uploadId,
partETags);
ossClient.completeMultipartUpload(request);
String downLoadUrl = getDownloadUrl(fileKey, bucketName);
logger.debug("-------------- 文件的下载URL ------------" + downLoadUrl);
return downLoadUrl;
}
/**
* 分片上传
*/
public PartETag partUploadFile(String fileKey, InputStream is, String uploadId, String fileMd5, int partNum,
long partSize) {
UploadPartRequest uploadPartRequest = new UploadPartRequest();
uploadPartRequest.setBucketName(bucketName);
uploadPartRequest.setUploadId(uploadId);
uploadPartRequest.setPartNumber(partNum);
uploadPartRequest.setPartSize(partSize);
uploadPartRequest.setInputStream(is);
uploadPartRequest.setKey(fileKey);
UploadPartResult uploadPartResult = ossClient.uploadPart(uploadPartRequest);
return uploadPartResult.getPartETag();
}
/**
* 初始化上传,获取UploadId
*/
public String getUploadId(String fileKey) {
InitiateMultipartUploadRequest request = new InitiateMultipartUploadRequest(bucketName, fileKey);
// 初始化分片
InitiateMultipartUploadResult unrest = ossClient.initiateMultipartUpload(request);
// 返回uploadId,它是分片上传事件的唯一标识,您可以根据这个ID来发起相关的操作,如取消分片上传、查询分片上传等。
String uploadId = unrest.getUploadId();
return uploadId;
}
}
分片上传时调用阿里云分片上传:
/ /分片上传
boolean flag = uploadChunkToOSS(fullFileName, fileChunkDTO);
方法调用:
//工具类
private final OSSUtil ossUtil;
//临时存储PartETag---合并时候需要
List<PartETag> partETags = new LinkedList<>();
//临时存储uploadId----实际应该提供获取uploadId的接口,前端调用后,每次分片上传都传过来
private Map<String, String> map1 = new HashMap<>();
public Boolean uploadChunkToOSS(String fullFileName,FileChunkDTO fileChunkDTO) {
//必须求出redis中的PartETags,在分片合成文件中需要以此为依据,合并文件返回最终地址
String uploadId = map1.get("uploadId");
if (uploadId == null){
uploadId = ossUtil.getUploadId(fullFileName);
map1.put("uploadId",uploadId);
}
int sliceNo = fileChunkDTO.getChunkNumber();
int fileSlicesNum = fileChunkDTO.getTotalChunks();
//分片上传
try {
//字节流转换
InputStream inputStream = fileChunkDTO.getFile().getInputStream();
//每次上传分片之后,OSS的返回结果会包含一个PartETag
PartETag partETag = ossUtil.partUploadFile(fullFileName, inputStream, uploadId,
fileChunkDTO.getIdentifier(), sliceNo, fileChunkDTO.getFile().getSize());
partETags.add(partETag);
//分片编号等于总片数的时候合并文件,如果符合条件则合并文件,否则继续等待
if (fileSlicesNum==sliceNo) {
//合并文件,注意:partETags必须是所有分片的所以必须存入redis,然后取出放入集合
String url = ossUtil.completePartUploadFile(fullFileName, uploadId,
partETags);
//oss地址返回后存入并清除redis
System.out.println(url);
partETags.clear();
}
} catch (Exception e) {
}
return true;
}