全部產品
Search
文件中心

Object Storage Service:使用Node.js分區上傳大檔案到OSS

更新時間:Feb 28, 2024

OSS提供的分區上傳(Multipart Upload)功能,將要上傳的較大檔案(Object)分成多個分區(Part)來分別上傳,上傳完成後再調用CompleteMultipartUpload介面將這些Part組合成一個Object來達到斷點續傳的效果。

背景資訊

當需要上傳的檔案較大時,您可以通過MultipartUpload方法進行分區上傳。分區上傳是指將要上傳的檔案分成多個資料區塊(Part)來分別上傳。當其中一些分區上傳失敗後,OSS將保留上傳進度記錄。當您再次重傳時,只需要上傳失敗的分區,不需要重新上傳整個檔案。

重要

通常在檔案大於100 MB的情況下,建議採用分區上傳的方法,通過斷點續傳和重試,提高上傳成功率。如果在檔案小於100 MB的情況下使用分區上傳,且partSize設定不合理的情況下,可能會出現無法完整顯示上傳進度的情況。對於小於100 MB的檔案,建議使用簡單上傳的方式。

在使用MultipartUpload方法時,如果遇到ConnectionTimeoutError逾時問題,業務方需自行處理逾時邏輯。例如通過縮小分區大小、增加逾時時間、重試請求或者捕獲ConnectionTimeoutError錯誤等方法處理逾時。更多資訊,請參見網路錯誤處理

分區上傳涉及的相關參數說明請參見下表。

類型

參數

說明

必選參數

name {String}

Object完整路徑,Object完整路徑中不能包含Bucket名稱。

file {String|File}

表示檔案路徑或者HTML5檔案。

[options] {Object} 選擇性參數

[checkpoint] {Object}

記錄本地分區上傳結果的檔案。開啟斷點續傳功能時需要設定此參數,上傳過程中的進度資訊會儲存在該檔案中,如果某一分區上傳失敗,再次上傳時會根據檔案中記錄的點繼續上傳。上傳完成後,該檔案會被刪除。

[parallel] {Number}

並發上傳的分區個數,預設值為5。如果無特殊需求,無需手動設定此參數。

[partSize] {Number}

指定上傳的每個分區的大小,範圍為100 KB~5 GB。單個分區預設大小為1 * 1024 * 1024(即1 MB)。如果無特殊需求,無需手動設定此參數。

[progress] {Function}

表示進度回呼函數,用於擷取上傳進度,可以是async的函數形式。回呼函數包含三個參數:

  • percentage {Number}:進度百分比(0~1之間的小數)。

  • checkpoint {Object}:與[options] {Object}中的[checkpoint] {Object}定義相同。

  • res {Object}:單個分區上傳成功返回的response。

[meta] {Object}

使用者自訂的Header meta資訊,Header首碼為x-oss-meta-

[mime] {String}

設定Content-Type要求標頭。

[headers] {Object}

其他Header。更多資訊,請參見RFC 2616。其中:

  • 'Cache-Control':通用訊息頭被用在HTTP請求和響應中通過指定指令來實現緩衝機制,例如Cache-Control: public, no-cache

  • 'Content-Disposition':指示回複的內容該以何種形式展示,是以網頁預覽的形式,還是以附件的形式下載並儲存到本地,例如Content-Disposition: somename

  • 'Content-Encoding':用於對特定媒體類型的資料進行壓縮,例如Content-Encoding: gzip

  • 'Expires':緩衝內容的到期時間,單位為毫秒。

分區上傳完整樣本

重要

Node.js分區上傳過程中不支援MD5校正。建議分區上傳完成後調用CRC64庫自行判斷是否進行CRC64校正。

以下代碼通過multipartUpload方法進行分區上傳。

const OSS = require('ali-oss');
const path = require("path");

const client = new OSS({
  // yourregion填寫Bucket所在地區。以華東1(杭州)為例,Region填寫為oss-cn-hangzhou。
  region: 'yourregion',
  // 從環境變數中擷取訪問憑證。運行本程式碼範例之前,請確保已設定環境變數OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
  accessKeyId: process.env.OSS_ACCESS_KEY_ID,
  accessKeySecret: process.env.OSS_ACCESS_KEY_SECRET,
  // 填寫儲存空間名稱。
  bucket: 'yourbucketname'
});


const progress = (p, _checkpoint) => {
  // Object的上傳進度。
  console.log(p); 
  // 分區上傳的斷點資訊。
  console.log(_checkpoint); 
};

const headers = {  
  // 指定Object的儲存類型。
  'x-oss-storage-class': 'Standard', 
  // 指定Object標籤,可同時設定多個標籤。
  'x-oss-tagging': 'Tag1=1&Tag2=2', 
  // 指定初始化分區上傳時是否覆蓋同名Object。此處設定為true,表示禁止覆蓋同名Object。
  'x-oss-forbid-overwrite': 'true'
}

// 開始分區上傳。
async function multipartUpload() {
  try {
    // 依次填寫Object完整路徑(例如exampledir/exampleobject.txt)和本地檔案的完整路徑(例如D:\\localpath\\examplefile.txt)。Object完整路徑中不能包含Bucket名稱。
    // 如果本地檔案的完整路徑中未指定本地路徑(例如examplefile.txt),則預設從樣本程式所屬專案對應本地路徑中上傳檔案。
    const result = await client.multipartUpload('exampledir/exampleobject.txt', path.normalize('D:\\localpath\\examplefile.txt'), {
      progress,
      // headers,
      // 指定meta參數,自訂Object的中繼資料。通過head介面可以擷取到Object的meta資料。
      meta: {
        year: 2020,
        people: 'test',
      },
    });
    console.log(result);
    // 填寫Object完整路徑,例如exampledir/exampleobject.txt。Object完整路徑中不能包含Bucket名稱。
    const head = await client.head('exampledir/exampleobject.txt');
    console.log(head);
  } catch (e) {
    // 捕獲逾時異常。
    if (e.code === 'ConnectionTimeoutError') {
      console.log('TimeoutError');
      // do ConnectionTimeoutError operation
    }
    console.log(e);
  }
}

multipartUpload();

以上分區上傳完整樣本調用的方法multipartUpload中封裝了初始化分區上傳、上傳分區以及完成分區上傳三個API介面。如果您希望分步驟實現分區上傳,請依次調用.initMultipartUpload.uploadPart以及.completeMultipartUpload方法。

取消分區上傳事件

您可以調用client.abortMultipartUpload方法來取消分區上傳事件。當一個分區上傳事件被取消後,無法再使用該uploadId做任何操作,已經上傳的分區資料會被刪除。

以下代碼用於取消分區上傳事件。

const OSS = require("ali-oss");

const client = new OSS({
  // yourregion填寫Bucket所在地區。以華東1(杭州)為例,Region填寫為oss-cn-hangzhou。
  region: "yourregion",
  // 從環境變數中擷取訪問憑證。運行本程式碼範例之前,請確保已設定環境變數OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
  accessKeyId: process.env.OSS_ACCESS_KEY_ID,
  accessKeySecret: process.env.OSS_ACCESS_KEY_SECRET,
  // 填寫儲存空間名稱。
  bucket: "yourbucketname",
});

async function abortMultipartUpload() {
  // 填寫Object完整路徑,例如exampledir/exampleobject.txt。Object完整路徑中不能包含Bucket名稱。
  const name = "exampledir/exampleobject.txt";
  // 填寫uploadId。uploadId來源於調用InitiateMultipartUpload完成初始化分區之後的返回結果。
  const uploadId = "0004B999EF518A1FE585B0C9360D****";
  const result = await client.abortMultipartUpload(name, uploadId);
  console.log(result);
}

abortMultipartUpload();

列舉分區上傳事件

調用client.listUploads方法列舉出所有執行中的分區上傳事件,即已初始化但尚未完成或尚未取消的分區上傳事件。

const OSS = require("ali-oss");

const client = new OSS({
  // yourregion填寫Bucket所在地區。以華東1(杭州)為例,Region填寫為oss-cn-hangzhou。
  region: "yourregion",
  // 從環境變數中擷取訪問憑證。運行本程式碼範例之前,請確保已設定環境變數OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
  accessKeyId: process.env.OSS_ACCESS_KEY_ID,
  accessKeySecret: process.env.OSS_ACCESS_KEY_SECRET,
  // 填寫儲存空間名稱。
  bucket: "yourbucketname",
});

async function listUploads(query = {}) {
  // query中支援設定prefix、marker、delimiter、upload-id-marker和max-uploads參數。
  const result = await client.listUploads(query);

  result.uploads.forEach((upload) => {
    // 分區上傳的uploadId。
    console.log(upload.uploadId);
    // 將所有上傳完成後的分區(Part)組合為一個完整的Object,並指定Object完整路徑。
    console.log(upload.name);
  });
}

const query = {
  // 指定此次返回Multipart Uploads事件的最大個數。max-uploads參數的預設值和最大值均為1000。
  "max-uploads": 1000,
};
listUploads(query);

列舉已上傳的分區

分區上傳過程中,調用client.listParts方法列舉指定uploadId下所有已經上傳成功的分區。

const OSS = require("ali-oss");

const client = new OSS({
  // yourregion填寫Bucket所在地區。以華東1(杭州)為例,Region填寫為oss-cn-hangzhou。
  region: "yourregion",
  // 從環境變數中擷取訪問憑證。運行本程式碼範例之前,請確保已設定環境變數OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
  accessKeyId: process.env.OSS_ACCESS_KEY_ID,
  accessKeySecret: process.env.OSS_ACCESS_KEY_SECRET,
  // 填寫儲存空間名稱。
  bucket: "yourbucketname",
});

async function listParts() {
  const query = {
    // 指定此次返回的最大分區(Part)個數。max-parts參數的預設值和最大值均為1000。
    "max-parts": 1000,
  };
  let result;
  do { 
    result = await client.listParts(
      // 填寫Object完整路徑(例如exampledir/exampleobject.txt)。Object完整路徑中不能包含Bucket名稱。
      "exampledir/exampleobject.txt",
      // uploadId來源於調用InitiateMultipartUpload完成初始化分區之後,且在調用CompleteMultipartUpload完成分區上傳之前的返回結果
      "0004B999EF518A1FE585B0C9360D****",
      query
    );
    // 指定下次列舉分區的起始位置,只有分區號大於此參數值的分區會被列舉。
    query["part-number-marker"] = result.nextPartNumberMarker;
    result.parts.forEach((part) => {
      console.log(part.PartNumber);
      console.log(part.LastModified);
      console.log(part.ETag);
      console.log(part.Size);
    });
  } while (result.isTruncated === "true");
}

listParts();

常見問題

通過分區上傳的方式上傳檔案後,如何擷取整個檔案的MD5值?

通過CompleteMultipartUpload將分區合成完整Object時會在響應Header中返回Content-MD5作為整個檔案的MD5值。

相關文檔

  • 關於分區上傳的完整範例程式碼,請參見GitHub樣本

  • Node.js SDK分區上傳調用的方法multipartUpload中封裝了三個API介面,詳情如下:

  • 關於取消分區上傳事件的API介面說明,請參見AbortMultipartUpload

  • 關於列舉已上傳分區的API介面說明,請參見ListParts

  • 關於列舉所有執行中的分區上傳事件(即已初始化但尚未完成或尚未取消的分區上傳事件)的API介面說明,請參見ListMultipartUploads