Hướng dẫn dùng API upload file có dung lượng lớn lên Sun S3#

Tổng quan:#

Với việc upload file bình thường, người dùng cần thực hiện ít nhất 3 request sau:

  1. Đầu tiên gọi API Pre-upload (/s3/v1/object/pre-upload) để lấy presigned URL

  2. Gọi phương thức PUT để upload file qua URL vừa nhận được để Object được upload trực tiếp lên Sun S3

  3. Gọi API Post-upload để hoàn thành upload

Khi file muốn tải lên là file nặng (>=100MB), bạn nên sử dụng cơ chế upload thành nhiều phần (multipart upload) để:

  • Tránh việc bị Request Time Out

  • Upload đồng thời các phần sẽ cải thiện tốc độ

  • Có thể Stop và Resume việc upload và retry được 1 part bị lỗi mà k ảnh hưởng đến cả quá trình

Step

Chúng ta sẽ tìm hiểu cụ thể cơ chế qua các bước. Ở đây bài viết sẽ dùng ReactJS để minh họa đơn giản cách làm, bạn hãy tham khảo để áp dụng linh hoạt vào project của mình.

Trước khi làm bạn nên mở thêm 1 tab API List để tiện tra cứu.

Step

Thực hiện#

Bước 1: Preview#

  • Để có thể preview thuận tiện, bạn có thể sử dụng thư viện hỗ trỡ việc lấy dữ liệu file lồng bên trong cây thư mục của folder kéo vào như “react-dropzone”, thư viện này còn hỗ trợ việc kéo thả Drag and Drop file và folder.

  • Giờ sẽ tiến hành chia file để upload. Đầu tiên ta xác định dung lượng mỗi part, ở đây mình đặt là 50MB, sau đó xác định số part:

const FILE_SIZE_THRESH_SI = 1000;
const UPLOAD_CHUNK_SIZE = 50 * FILE_SIZE_THRESH_SI ** 2;
const partNum = Math.ceil(f.file.size / UPLOAD_CHUNK_SIZE);

Nếu số part > 1, người dùng sử dụng multipart, enum 2 loại upload như sau:

enum UploadTypeEnum {
  multiPart = "MultiplePart",
  singlePart = "SinglePart",
}

Từ đó có payload gọi API Pre-upload (/s3/v1/object/pre-upload) như sau:

{
      bucketUniqcode: bucketUniqcode,
      items: prepareFiles.map((f) => {
        // detect multi parts upload
        const partNum = Math.ceil(f.file.size / UPLOAD_CHUNK_SIZE);
 
        return {
          name: f.file.name,
          folderPath: currentFolderPath,
          type: partNum > 1 ? UploadTypeEnum.multiPart : UploadTypeEnum.singlePart,
          numberOfParts: partNum > 1 ? partNum : undefined,
        };
      }),
    }

Sau đó response sẽ trả về list các presigned URL để người dùng chuẩn bị upload

Bước 2: Upload multipart lên SunS3#

Hãy xem đoạn code tham khảo sau đây:

const upPartPromises = (partItems ?? []).map(async (part) => {
  const start = ((part?.partNo ?? 1) - 1) * UPLOAD_CHUNK_SIZE;
  const end = (part?.partNo ?? 1) * UPLOAD_CHUNK_SIZE;
  const blob = hisFile.file.slice(start, end);
 
  const promise = axios.put(
    url: part.uploadUrl ?? "",
    upFile: blob,
    {
      headers: {
        "Content-Type": "multipart/form-data",
      },
      onUploadProgress: (pEvent: ProgressEvent) => {
       if (pEvent.loaded === pEvent.total) {
         setProgress((pre) => pre + MAX_PERCENT / partNum);
       }
      },
    });
     
   
 
  return promise;
});
 
const resList = await Promise.all(upPartPromises);
  • Giải thích:

    • Đầu tiên ta chia file thành các blob dữ liệu, với mỗi blob sẽ upload lên 1 URL

    • Header có “Content-Type”: “multipart/form-data”, phương thức này còn giúp theo dõi được onUploadProgress để theo dõi % hoàn thành của từng part chính xác hơn

    • Như ở code mẫu sinh ra 1 mảng promise để có thể upload đồng thời các part cùng lúc, bạn cũng có thể kết hợp thêm batching khi số part cần upload nhiều lên

Bước 3: Complete Upload#

Sau khi upload thành công tất cả các part, người dùng cần có thêm bước xác nhận lại với hệ thống để ghép lại hoàn chỉnh thành Object

Sử dụng API Post-Upload (/s3/v1/object/post-upload)

Result:

Sau khi hoàn thành, Object mong muốn sẽ được thêm vào Bucket, bạn có thể lên console của Sun S3 để kiểm tra lại.

Step

Reference:

Hình minh họa đầu bài viết tham khảo từ

https://magz.techover.io/2021/08/28/upload-file-dung-luong-lon-toi-s3-voi-multipart-va-presign-url/