[Spring] AWS S3 Multipart Upload - REST API


より大きなファイルアップロードを実現するため、Google検索を行った結果、AWS 3は大きなファイルアップロード時に複数のアップロードをサポートしていることが分かった.しかし、公式文書を除いては、グーグルで理解できる資料を見つけることが難しく、数日以内に様々な試みを行い、最終的に成功した後、文章を残した.参考までに筆者の開発環境はJava Spring MVCである.
まず、私が実現したい機能は、パンツやKakaoTalkなどにファイルをアップロードすることで、ファイルの進捗を見て、順番にファイルをアップロードすることができ、予想されるファイルのアップロードサイズは最高10 GBに達することができます.

AWS公式ファイルによると、複数の部分をアップロードするには3つのステップが必要です.
  • Multipart upload initiation
  • Parts upload
  • Multipart upload completion
  • 1. Multipart upload initiation


    Initiate requestがAWSに送信されると、S 3にアップロードが登録され、一意のidが送信される.
    InitiateMultipartUploadResult initiateUpload =
                    s3Client.initiateMultipartUpload(
                            new InitiateMultipartUploadRequest(BUCKET_NAME, KEY));
    log.info("upload id : {}", initiateUpload.getUploadId());
    このときKEYはAWS 3にアップロードするファイル名である.ex) dir/object.mp4
    次に、partsアップロードフェーズで、受信するETagを含む配列を作成します.
    private List<PartETag> partETags = new ArrayList<>();

    2. Parts upload


    アップロードするファイルをブロック化し、リクエストを個別に送信します.
    各リクエストは、部分に対応するETag値を返します.
    これらのETagを集めて、完成したら送ってください.
  • 各要求は、発起人から受信された一意のIDを含むべきである.
  • 個の部分ごとに部分番号を明記します.部品番号は1から10000まで可能で、連続する必要はなく、順次増加するだけです.例えば、部品番号が[1,2,3,4,5]でなくても[1,3,5,7,9]は正常に動作する.
  • の部分は少なくとも5 MB以上でなければならない.(最後の部分を含まない、最後の部分が5 MB未満)
  • の場合、5 MBは5 MIB=5*1024*1024バイトである.
  • UploadPartRequest uploadPartRequest = new UploadPartRequest()
                        .withBucketName(BUCKET_NAME) // 버킷 이름
                        .withKey(KEY) // s3에 올라갈 파일 경로+이름
                        .withUploadId(upload_id) // init에서 받은 id
                        .withPartNumber(part_number) // 순차적인 part number (1~10,000)
                        .withFile(part_file) // chunk file
                        .withPartSize(part_file.length()); // chunk file size
    
    UploadPartResult uploadPartResult = s3Client.uploadPart(uploadPartRequest);
    partETags.add(uploadPartResult.getPartETag()); // init때 만든 배열에 ETag 담기

    3. Multipart upload completion


    すべての部分のアップロードが完了したら、最後にAWSに完了要求を送信し、ファイルの保存を完了します.フルリクエストを送信しないと、ファイルは生成されません.
    s3Client.completeMultipartUpload(
                        new CompleteMultipartUploadRequest(
                                BUCKET_NAME,
                                KET,
                                upload_id,
                                partETags)); // List<PartEtag>
    completeの後S 3 Bucketをチェックすると、ファイルがよく含まれていることがわかります.

    💡 注意事項


    1. Front code


    ファイルをフロントからChunkに切り取るコード
    const CHUNK_SIZE = 5 * 1024 * 1024; // 5MiB
    let part_index = 1, flag = 0;
    const origin_file = input_file_element.files[0];
    
    function partUpload(upload_id) {
      const blob = origin_file.slice(flag, Math.min(origin_file.size, flag + CHUNK_SIZE));
      const reader = new FileReader();
      reader.readAsDataURL(blob);
      reader.onloadend = async () => {
        if (flag >= origin_file.size) { // 전송완료
          const completed = await completeUpload(upload_id);
          return;
        }
    
        const success = await sendEncodedByteData(upload_id, reader.result);
        if (success) { // 전송 성공
          byte_flag += CHUNK_SIZE;
          part_index += 1;
          partUpload(upload_id); // recursive
        } else { // 전송중 에러
          // error handle
        }
      }
    }
    サーバへの送信時にBase 64としてエンコードされます.
    コントローラはStringを受信し、Base 64 Decodeを介してbyte[]に変換する.

    2. Abort


    Initiate以降、エラー発生、アップロード停止等により完了できない場合、そのアップロードIDの多くのアップロードはS 3に残り続ける.これでは有料になる可能性があるので、必ず完成または中止しなければならないと、AWSファイルで述べています.
    s3Client.abortMultipartUpload(
    	new AbortMultipartUploadRequest(
        BUCKET_NAME,
        KEY, 
        upload_id)); // return void
    

    3.残りのアップロードの表示


    開発テストでは、未完了および未終了のアップロードを処理する必要があります.
    MultipartUploadListing multipartUploadListing = 
    	s3Client.listMultipartUploads(new ListMultipartUploadsRequest(BUCKET_NAME);
    
    List<MultipartUpload> multipartUploads = multipartUploadListing.getMultipartUploads();
    for (MultipartUpload multipartUpload : multipartUploads) {
    	log.info("upload id: {}, key: {}", multipartUpload.getUploadId(), multipartUpload.getKey());
        // abort(upload id, key)
    }

    4.その他

  • の間で一時停止すると、このアップロードIDは利用できないという.
  • にアップロードが完了すると、アップロードされたビデオファイルはアップロードされた場所にしか再生できません.
  • 各部分ファイルのサイズは少なくとも5 MBから5 GBであるため、最大5 GiB*10000=5 TBまでアップロードできる.
  • は現在、中断時にファイルアップロードを廃棄することが実現されているが、これらの機能を十分に利用すれば、大きなファイルアップロードの一時停止と復元機能を実現することができる.
  • サーバ側Heap Sizeの安定性テストと管理が必要なようです.
  • マルチスレッドを使用して、ファイルのアップロード速度を速めることができます.
  • 2AWS JavaScript SDKを使用してフロントエンドから直接アップロードを実行すれば、サーバ負荷を大幅に緩和できます.
  • Reference :
    AWS S3 Multipart upload doc
    https://wave1994.tistory.com/152