+10

Tối ưu upload file dung lượng lớn lên S3 bằng API

Ở bài trước, mình đã giới thiệu chi tiết và đơn giản về bản chất của 3 loại Cloud Storage. Trong đó có Object storage là được ứng dụng rộng rãi nhất trong việc tích hợp với Web App, ứng dụng. Để xen kẽ với bài viết chia sẻ lý thuyết, mình sẽ giới thiệu chút thực hành cho các bạn biết code chút mong muốn ứng dụng luôn vào thực tế.

Ở bài viết này, mình sẽ sử dụng dịch vụ lưu trữ S3 Sun Simple Storage (Sun S3). Nếu các bạn muốn upload đơn giản hơn bằng giao diện, không cần tích hợp API thì có thể xem ở trang doc chính thức của Sunteco ở đây: https://docs.sunteco.vn/service/s3/how-to/hd-upload-object-bang-giao-dien.html

Còn sau đây sẽ là cách tích hợp API.

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

Giải thích thêm: Tại sao cần 3 bước với các API khác nhau mà không gộp chung lại làm 1 cái. Vì file dung lượng lớn nếu qua phía Backend rồi mới tới Sun S3 sẽ phải qua 2 lần upload sẽ tốn kém và tốc độ thấp hơn. Thêm nữa, việc mở ra chi tiết các API giúp bạn có thể thao tác, quản lý các part upload của mình.

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

image.png

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 trên trang dashboard sản phẩm https://dashboard.sunteco.vn/ để tiện tra cứu:

image.png

Steps:

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,
        };
      }),
    }

Bước 2: Upload multipart lên Sun S3

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 = file.slice(start, end); // file với type là File mà bạn up lên, tham khảo: https://developer.mozilla.org/en-US/docs/Web/API/Blob/slice
 
  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

Sau đó response sẽ trả về list các presigned URL để người dùng chuẩn bị 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 trang dashboard của Sun S3 để kiểm tra lại.

image.png

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/

Trang demo: dashboard.sunteco.vn


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí