AWS之S3文件上传(分块文件上传)

背景

文件分块上传,其实就是AWS开一个上传任务,然后,客户端并发把切割后的文件块上传到同一个上传任务中。当所有文件块都上传完成时,客户端告诉AWS上传任务完成即可。

思路

主要就是3步:

  • 开启上传任务
  • 切割文件块,并发上传文件块
  • 完成上传任务

这里的最后一步完成上传任务,其实就是s3把所有文件块合并成一个大文件。

方案

这里主要实现服务端三步逻辑,即开启上传任务,文件块上传,解释上传任务。

开启上传任务

  @Override
  public BaseResponse initUploadFileTask(InitUploadFileTaskReq initUploadFileTaskReq) {
    try {
      String bucketName = initUploadFileTaskReq.getBucketName();
      String keyName = initUploadFileTaskReq.getKeyName();

      handBucket(bucketName, s3Client);

      CreateMultipartUploadRequest.Builder createMultipartUploadRequestBuilder =
          CreateMultipartUploadRequest.builder();
      createMultipartUploadRequestBuilder
          .bucket(bucketName)
          .key(keyName)
          .storageClass(StorageClass.INTELLIGENT_TIERING);

      String contentType = initUploadFileTaskReq.getContentType();
      if (StringUtils.hasText(contentType)) {
        createMultipartUploadRequestBuilder.contentType(initUploadFileTaskReq.getContentType());
      }

      // 开启上传任务
      CreateMultipartUploadResponse createMultipartUploadResponse =
          s3Client.createMultipartUpload(createMultipartUploadRequestBuilder.build());

      log.info(
          "开始上传任务请求参数:{}, 返回任务ID:{}",
          initUploadFileTaskReq,
          createMultipartUploadResponse.uploadId());
      return BaseResponse.builder()
          .data(
              InitUploadFileTaskRes.builder()
                  .uploadId(createMultipartUploadResponse.uploadId())
                  .build())
          .build();
    } catch (SdkClientException e) {
      log.error("异常开始上传任务请求参数:{}", initUploadFileTaskReq);
      throw new HandleException(
          String.format(
              "异常开始上传任务请求参数:%s, 异常:%s", initUploadFileTaskReq.toString(), e.getMessage()));
    }
  }

这里的InitUploadFileTaskReqBaseResponse是DTO层对象,不可考虑,这里针对桶名和key名开启分块上传任务,并返回任务ID给前端。其他初始化s3的客户端,已经构建分块上传任务请求逻辑,和之前文章《AWS之S3文件上传(简单文件上传)》中类似。这里开启分块上传任务关键代码行如下:

CreateMultipartUploadResponse createMultipartUploadResponse =
          s3Client.createMultipartUpload(createMultipartUploadRequestBuilder.build());

主要从CreateMultipartUploadResponse中获得任务id。

分块上传

  @Override
  public BaseResponse uploadMultipartFile(UploadFileReq req) {
    String bucketName = req.getBucketName();
    String keyName = req.getKeyName();
    String uploadId = req.getUploadId();
    int partNumber = req.getPartNumber();

    MultipartFile multipartFile = req.getFile();

    File tmpFile = null;
    try {
      tmpFile = File.createTempFile("uploadFileTemp", ".tmp");
      multipartFile.transferTo(tmpFile);

      UploadPartRequest uploadRequest =
          UploadPartRequest.builder()
              .bucket(bucketName)
              .key(keyName)
              .uploadId(uploadId)
              .partNumber(partNumber)
              .contentLength(tmpFile.length())
              .build();

      RequestBody requestBody = RequestBody.fromFile(tmpFile);

      // 上传分块文件
      UploadPartResponse uploadPartResponse = s3Client.uploadPart(uploadRequest, requestBody);
      log.info("上传文件段对象:{}-返回ETag对象:{}", req, uploadPartResponse.eTag());
      return BaseResponse.builder()
          .data(
              PartETagRes.builder()
                  .eTag(uploadPartResponse.eTag())
                  .partNumber(partNumber)
                  .build())
          .build();
    } catch (SdkClientException | IOException e) {
      log.error("上传文件段对象异常:{},错误:{}", req, e);

      throw new HandleException(
          String.format("上传文件段对象异常:%s,错误:%s", req.toString(), e.getMessage()));
    } finally {
      if (tmpFile != null) {
        if (tmpFile.exists()) {
          if (!tmpFile.delete()) {
            log.error(String.format("临时文件删除失败!%s", tmpFile.getAbsolutePath()));
          }
        }
      }
    }
  }

这里关键行如下:

UploadPartResponse uploadPartResponse = s3Client.uploadPart(uploadRequest, requestBody);

从分块上传UploadPartResponse中获取到eTag值,并告诉前端即可。
这里到partNumber是文件分块顺序,即当前子文件块在母文件中排序是多少。所以,前端需要记住子文件块数组中,每个子文件块对应的eTag值为多少。

结束上传任务

  @Override
  public BaseResponse completeMultipartUploadFile(CompleteUploadFileReq req) {
    try {
      String bucketName = req.getBucketName();
      String keyName = req.getKeyName();
      String uploadId = req.getUploadId();
      List<PartETagRes> partETagResList = req.getPartETagResList();
      List<CompletedPart> partETags =
          partETagResList.stream()
              .map(
                  partETagRes ->
                      CompletedPart.builder()
                          .partNumber(partETagRes.getPartNumber())
                          .eTag(partETagRes.getETag())
                          .build())
              .collect(Collectors.toList());

      CompleteMultipartUploadRequest completeMultipartUploadRequest =
          CompleteMultipartUploadRequest.builder()
              .bucket(bucketName)
              .key(keyName)
              .uploadId(uploadId)
              .multipartUpload(CompletedMultipartUpload.builder().parts(partETags).build())
              .build();
        // 完成上传任务
      CompleteMultipartUploadResponse completeMultipartUploadResponse =
          s3Client.completeMultipartUpload(completeMultipartUploadRequest);
      log.info("完成合并文件段请求参数:{}-返回结构:{}", req, completeMultipartUploadResponse.toString());
      return BaseResponse.builder()
          .data(
              CompleteMultipartUploadFileRes.builder()
                  .url(String.format("%s/%s", bucketName, keyName))
                  .name(keyName2FileName(keyName))
                  .build())
          .build();
    } catch (SdkClientException e) {

      log.error(String.format("请求参数%s, 完成合并文件段请求异常:", req.toString()), e);
      throw new HandleException(
          String.format("完成合并文件段请求参数:%s, 异常:%s", req.toString(), e.getMessage()));
    }
  }

结束上传任务关键行为:

CompleteMultipartUploadResponse completeMultipartUploadResponse =
          s3Client.completeMultipartUpload(completeMultipartUploadRequest);

前端需要准备List<CompletedPart>的数据,即每个分块的序号和eTag数据,s3需要依赖这个信息来合并文件。

总结

到这里就完成s3分块上传任务实现了,主要就是3个步骤。开启任务,并发上传分块,结束任务。AWS的S3文件上传暂时搞一段落了。

参考

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要将大文件上传AWS S3,可以使用AWS提供的multipart上传方法。这个方法允许你将文件分成多个部分来上传,以便在网络不稳定的情况下保证上传的可靠性。以下是一个简单的Node.js示例代码: ```javascript const AWS = require('aws-sdk'); const fs = require('fs'); // AWS S3配置 const s3 = new AWS.S3({ accessKeyId: 'your_accessKeyId', secretAccessKey: 'your_secretAccessKey' }); // 文件上传配置 const uploadParams = { Bucket: 'your_bucket_name', Key: 'your_file_name', Body: '', ContentType: 'your_content_type' }; // 分块上传文件 const fileStream = fs.createReadStream('your_file_path'); fileStream.on('error', function(err) { console.log('Error:', err); }); uploadParams.Body = fileStream; const multipartUpload = new AWS.S3.ManagedUpload({ params: uploadParams, partSize: 5 * 1024 * 1024, // 分块大小,单位:字节 queueSize: 1 // 并发上传文件数量 }); multipartUpload.on('httpUploadProgress', function(progress) { console.log('Progress:', progress); }); multipartUpload.promise().then(function(data) { console.log('Success:', data); }).catch(function(err) { console.log('Error:', err); }); ``` 在上面的代码中,我们使用AWS SDK来创建S3对象,并设置了访问密钥等参数。接着,我们设置了文件上传的配置参数,包括Bucket、Key、Body、ContentType等。最后,我们使用ManagedUpload类实现将文件分块上传到S3的功能,分块大小和并发上传文件数量可以根据实际需求进行调整。在上传过程中,我们可以通过httpUploadProgress事件获取上传进度信息。最后,我们使用promise()方法来等待文件上传完成,并处理上传成功或失败的结果。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值