大文件的OSS分片上传(完整代码示例)

目录

简介

分片上传思路图​编辑

oss分片上传代码示例

初始化分片事件

开始切片上传

分片上传完成,合并上传


简介

分片上传,就是将所要上传的文件,按照一定的大小,将整个文件分隔成多个数据块(称之为Part)来进行分别上传,上传完之后再由服务端对所有上传的文件进行汇总整合成原始的文件。

分片上传不仅可以避免因网络环境不好导致的一直需要从文件起始位置上传的问题,还能使用多线程对不同分块数据进行并发发送,提高发送效率。

分片上传思路图

  • 设置两个系统参数"是否分片"和"分片大小", 前者用于判断是否开启分片, 后者用于判断分片大小(不过此参数如果要修改,需要删除数据库未上传完的文件分片数据)
  • 串行可以文件追加(第一个文件分片上传之后,后面的文件分片都往此文件上追加)或者文件合并, 并行只能文件合并.
  • 某个节点上传失败后,客户端点击继续上传,客户调用查询上传进度接口.继续上传未上传完的文件分片

oss分片上传代码示例

初始化分片事件

向oss服务器获取全局唯一的uploadId, 如果之前上传到一半断了, 在数据库存储之前已上传的数据, 初始化时获取上传位置, 从指定位置开始上传

@Data
public class InitSliceDTO implements Serializable {
    private static final long serialVersionUID = 1L;
 
    @ApiModelProperty(value = "存储空间名称")
    private String bucketName;
 
    @ApiModelProperty(value = "对象名称")
    private String objectName;
 
    public InitSliceDTO() {
 
    }
 
    public InitSliceDTO(String bucketName, String objectName) {
        this.bucketName = bucketName;
        this.objectName = objectName;
    }
}
 
public class InitSliceResultDTO implements Serializable {
    private static final long serialVersionUID = 1L;
 
    @ApiModelProperty(value = "oss的uploadId")
    private String uploadId;
 
    @ApiModelProperty(value = "存储空间名称")
    private String bucketName;
 
    @ApiModelProperty(value = "对象名称")
    private String objectName;
 
    public InitSliceResultDTO() {
 
    }
 
    public InitSliceResultDTO(String uploadId, String bucketName, String objectName) {
        this.uploadId = uploadId;
        this.bucketName = bucketName;
        this.objectName = objectName;
    }
}
 
// 创建InitiateMultipartUploadRequest对象。
OSSClient ossClient = FileUploadUtil.getInstance();
InitiateMultipartUploadRequest request = new InitiateMultipartUploadRequest
        (initSliceDTO.getBucketName(), initSliceDTO.getObjectName());
InitiateMultipartUploadResult upresult = ossClient.initiateMultipartUpload(request);
return new InitSliceResultDTO(upresult.getUploadId(), initSliceDTO.getBucketName(), initSliceDTO.getObjectName());

开始切片上传

  • 分片一般由前端完成,通过后端将分片上传到oss服务器,上传时可无需按照顺序上传。
  • 如果由前端直接上传到oss服务器, 后端仅需要向前端颁发凭证即可。
  • 需要注意的是,在分片上传成功后需保存响应数据,合并时需要。
    public class ChunkDTO implements Serializable {
     
        private static final long serialVersionUID = 1L;
     
        @NotNull(message = "文件块序号不能为空")
        @Min(message = "文件块序号必须大于等于1", value = 1)
        @Max(message = "文件块序号必须小于等于10000", value = 10000)
        @ApiModelProperty(value = "当前文件块,从1开始")
        private Integer chunkNumber;
     
        @ApiModelProperty(value = "分块大小")
        @NotNull(message = "分块大小不能为空")
        private Long chunkSize;
     
        @ApiModelProperty(value = "当前分块大小")
        @NotNull(message = "当前分块大小不能为空")
        @Max(message = "当前分块大小不打大于5G", value = 5 * 1024 * 1024 * 1024)
        private Long currentChunkSize;
     
        @ApiModelProperty(value = "总大小")
        @NotNull(message = "总大小不能为空")
        private Long totalSize;
     
        @ApiModelProperty(value = "文件标识")
        @NotBlank(message = "文件标识不能为空")
        private String identifier;
     
        @ApiModelProperty(value = "文件名")
        @NotBlank(message = "文件名不能为空")
        private String filename;
     
        @ApiModelProperty(value = "相对路径")
        private String relativePath;
     
        @ApiModelProperty(value = "总块数")
        @NotNull(message = "总块数不能为空")
        private Integer totalChunks;
     
        @ApiModelProperty(value = "oss的uploadId")
        private String uploadId;
     
        @ApiModelProperty(value = "对象名称,也是path")
        private String objectName;
     
        @ApiModelProperty(value = "二进制文件")
        @NotNull(message = "二进制文件不能为空")
        private MultipartFile file;
    }
     
    public class ChunkUploadDTO extends ChunkDTO {
     
        @ApiModelProperty(value = "存储空间名称")
        private String bucketName;
     
        @ApiModelProperty(value = "用于完成时合并所需参数")
        private List<PartETagDTO> partETags;
    }
     
    @ApiModel(description = "对应PartETag")
    public class PartETagDTO implements Serializable {
        private static final long serialVersionUID = 1L;
     
        @ApiModelProperty(value = "分片序号")
        private int partNumber;
     
        @ApiModelProperty(value = "eTag")
        private String eTag;
     
        @ApiModelProperty(value = "分片大小")
        private long partSize;
     
        @ApiModelProperty(value = "主键")
        private Long partCRC;
    }
     
    public SliceUploadResultDTO sliceUpload(ChunkDTO chunkDTO) throws Exception {
            ChunkUploadDTO chunkUploadDTO = (ChunkUploadDTO) chunkDTO;
            SliceUploadResultDTO sliceUploadResultDTO = new SliceUploadResultDTO();
            try {
                OSSClient ossClient = FileUploadUtil.getInstance();
                String uploadId = chunkUploadDTO.getUploadId();
                UploadPartRequest uploadPartRequest = new UploadPartRequest();
                uploadPartRequest.setBucketName(chunkUploadDTO.getBucketName());
                uploadPartRequest.setKey(chunkUploadDTO.getObjectName());
                uploadPartRequest.setUploadId(uploadId);
                uploadPartRequest.setInputStream(chunkDTO.getFile().getInputStream());
                // 设置分片大小。除了最后一个分片没有大小限制,其他的分片最小为100 KB。上传时不会做校验,合并时会做校验
                uploadPartRequest.setPartSize(chunkDTO.getCurrentChunkSize());
                // 设置分片号。每一个上传的分片都有一个分片号,取值范围是1~10000,如果超出此范围,OSS将返回InvalidArgument错误码。
                uploadPartRequest.setPartNumber(chunkDTO.getChunkNumber());
                // 每个分片不需要按顺序上传,甚至可以在不同客户端上传,OSS会按照分片号排序组成完整的文件。
                UploadPartResult uploadPartResult = ossClient.uploadPart(uploadPartRequest);
                PartETag partETag = uploadPartResult.getPartETag();
     
                sliceUploadResultDTO.setResult(true);
                sliceUploadResultDTO.setPartCRC(partETag.getPartCRC());
                sliceUploadResultDTO.setPartETag(partETag.getETag());
                sliceUploadResultDTO.setPartSize(partETag.getPartSize());
                sliceUploadResultDTO.setPartNumber(partETag.getPartNumber());
                sliceUploadResultDTO.setObjectName(chunkUploadDTO.getObjectName());
                sliceUploadResultDTO.setUploadId(uploadId);
            } catch (OSSException oe) {
                sliceUploadResultDTO.setResult(false);
                logger.error("Caught an OSSException, which means your request made it to OSS, "
                        + "but was rejected with an error response for some reason.");
                logger.error("Error Message:" + oe.getErrorMessage());
                logger.error("Error Code:" + oe.getErrorCode());
                logger.error("Request ID:" + oe.getRequestId());
                logger.error("Host ID:" + oe.getHostId());
            } catch (ClientException ce) {
                sliceUploadResultDTO.setResult(false);
                logger.error("Caught an ClientException, which means the client encountered "
                        + "a serious internal problem while trying to communicate with OSS, "
                        + "such as not being able to access the network.");
                logger.error("ClientException Error Message:" + ce.getMessage());
            } catch (Exception e) {
                logger.error("Exception Error Message:" + e.getMessage());
                logger.error("Exception:" + JSONObject.toJSONString(e.getStackTrace()));
                sliceUploadResultDTO.setResult(false);
            }
            return sliceUploadResultDTO;
        }
    }

分片上传完成,合并上传

  • 分片上传完成后,需主动通知oss服务器合并文件。
@ApiModel(description = "分片上传结果")
public class SliceUploadResultDTO implements Serializable {
 
    private static final long serialVersionUID = 1L;
 
    @ApiModelProperty(value = "是否上传成功结果")
    private Boolean result;
 
    @ApiModelProperty(value = "是否合并完成成功结果")
    private Boolean completeResult;
 
    @ApiModelProperty(value = "uploadId")
    private String uploadId;
 
    @ApiModelProperty(value = "分片序号")
    private Integer partNumber;
 
    @ApiModelProperty(value = "分片大小")
    private Long partSize;
 
    @ApiModelProperty(value = "分片etag")
    private String partETag;
 
    @ApiModelProperty(value = "分片crc,oss有此值")
    private Long partCRC;
 
    @ApiModelProperty(value = "合并后存储空间")
    private String bucketName;
 
    @ApiModelProperty(value = "合并后objectName对应key,也是路径")
    private String objectName;
 
    @ApiModelProperty(value = "location")
    private String location;
 
    @ApiModelProperty(value = "合并后etag")
    private String eTag;
}
 
 
public SliceUploadResultDTO completeMultipartUpload(ChunkDTO chunkDTO) {
        ChunkUploadDTO chunkUploadDTO = (ChunkUploadDTO) chunkDTO;
        SliceUploadResultDTO sliceUploadResultDTO = new SliceUploadResultDTO();
        try {
            OSSClient ossClient = FileUploadUtil.getInstance();
            List<PartETag> partETags = new ArrayList<>();
            chunkUploadDTO.getPartETags().forEach(e -> {
                partETags.add(new PartETag(e.getPartNumber(), e.getETag()));
            });
            // 分片上传结束后,调用complete完成分片上传
            CompleteMultipartUploadRequest completeMultipartUploadRequest =
                    new CompleteMultipartUploadRequest(chunkUploadDTO.getBucketName(), chunkUploadDTO.getObjectName(),
                            chunkUploadDTO.getUploadId(), partETags);
            CompleteMultipartUploadResult completeResult =
                    ossClient.completeMultipartUpload(completeMultipartUploadRequest);
            // build返回参数
            sliceUploadResultDTO.setCompleteResult(true);
            sliceUploadResultDTO.setBucketName(completeResult.getBucketName());
            sliceUploadResultDTO.setObjectName(completeResult.getKey());
            sliceUploadResultDTO.seteTag(completeResult.getETag());
            sliceUploadResultDTO.setLocation(completeResult.getLocation());
        } catch (Exception e) {
            logger.error("Exception Error Message:" + e.getMessage());
            logger.error("oss completeMultipartUpload Exception" + JSONObject.toJSONString(e.getStackTrace()));
            sliceUploadResultDTO.setCompleteResult(false);
        }
        return sliceUploadResultDTO;
    }
}

关于前端的分片上传可以使用vue-simple-uploader。

以上就是分片上传的内容,欢迎大家来讨论指正。

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java SDK for OSS 提供了分片上传对象的功能,可以将大文件切分成多个小块进行上传,以提高上传速度和可靠性。以下是 Java SDK for OSS 分片上传的简单示例: ```java import com.aliyun.oss.OSS; import com.aliyun.oss.OSSClientBuilder; import com.aliyun.oss.model.*; import java.io.File; import java.util.ArrayList; import java.util.List; public class OSSMultipartUploadDemo { private static String endpoint = "yourEndpoint"; private static String accessKeyId = "yourAccessKeyId"; private static String accessKeySecret = "yourAccessKeySecret"; private static String bucketName = "yourBucketName"; private static String objectName = "yourObjectName"; private static String uploadFilePath = "yourUploadFilePath"; public static void main(String[] args) { // 创建OSSClient实例 OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret); // 初始化分片上传 InitiateMultipartUploadRequest initiateMultipartUploadRequest = new InitiateMultipartUploadRequest(bucketName, objectName); InitiateMultipartUploadResult initiateMultipartUploadResult = ossClient.initiateMultipartUpload(initiateMultipartUploadRequest); String uploadId = initiateMultipartUploadResult.getUploadId(); // 计算文件分片数 final long partSize = 1024 * 1024L; // 设置分片大小为1MB final File uploadFile = new File(uploadFilePath); long fileLength = uploadFile.length(); int partCount = (int) (fileLength / partSize); if (fileLength % partSize != 0) { partCount++; } // 上传分片 List<PartETag> partETags = new ArrayList<>(); for (int i = 0; i < partCount; i++) { long startPos = i * partSize; long curPartSize = (i + 1 == partCount) ? fileLength - startPos : partSize; UploadPartRequest uploadPartRequest = new UploadPartRequest(); uploadPartRequest.setBucketName(bucketName); uploadPartRequest.setKey(objectName); uploadPartRequest.setUploadId(uploadId); uploadPartRequest.setPartNumber(i + 1); uploadPartRequest.setInputStream(FileUtils.openInputStream(uploadFile)); uploadPartRequest.setPartSize(curPartSize); uploadPartRequest.setOffset(startPos); UploadPartResult uploadPartResult = ossClient.uploadPart(uploadPartRequest); partETags.add(uploadPartResult.getPartETag()); } // 完成分片上传 CompleteMultipartUploadRequest completeMultipartUploadRequest = new CompleteMultipartUploadRequest(bucketName, objectName, uploadId, partETags); CompleteMultipartUploadResult completeMultipartUploadResult = ossClient.completeMultipartUpload(completeMultipartUploadRequest); // 关闭OSSClient ossClient.shutdown(); System.out.println("File upload successfully!"); } } ``` 以上代码中,首先创建了一个 OSSClient 实例,然后调用 `initiateMultipartUpload` 方法初始化分片上传,并保存返回的 `uploadId`。接下来计算文件分片数,循环上传每个分片,并将每个分片的 `PartETag` 对象添加到 `partETags` 列表中。最后调用 `completeMultipartUpload` 方法完成分片上传。 需要注意的是,OSS 对象的大小必须大于100KB,否则不能进行分片上传。而且,分片上传必须按照指定的顺序上传,每个分片的大小必须相同。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值