OSS提供的分片上传(Multipart Upload)功能,将要上传的较大文件(Object)分成多个分片(Part)来分别上传,上传完成后再调用CompleteMultipartUpload接口将这些Part组合成一个Object来达到断点续传的效果。

背景信息

当需要上传的文件较大时,您可以通过MultipartUpload接口进行分片上传。分片上传是指将要上传的文件分成多个数据块(Part)来分别上传。当其中一些分片上传失败后,OSS将保留上传进度记录,再次重传时只需要上传失败的分片,而不需要重新上传整个文件。一般大于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校验。
const OSS = require('ali-oss');
const path = require("path");

const client = new OSS({
  // yourRegion填写Bucket所在地域。以华东1(杭州)为例,Region填写为oss-cn-hangzhou。
  region: 'yourRegion',
  // 阿里云账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM用户进行API访问或日常运维,请登录RAM控制台创建RAM用户。
  accessKeyId: 'yourAccessKeyId',
  accessKeySecret: 'yourAccessKeySecret',
  // 填写Bucket名称,例如examplebucket。
  bucket: 'examplebucket',
});


const progress = (p, _checkpoint) => {
  console.log(p); // Object的上传进度。
  console.log(_checkpoint); // 分片上传的断点信息。
};

// 开始分片上传。
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,
      // 指定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();

取消分片上传事件

您可以调用client.abortMultipartUpload方法来取消分片上传事件。当一个分片上传事件被取消后,无法再使用该uploadId做任何操作,已经上传的分片数据会被删除。

以下代码用于取消分片上传事件。

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

const client = new OSS({
  // yourRegion填写Bucket所在地域。以华东1(杭州)为例,Region填写为oss-cn-hangzhou。
  region: 'yourRegion',
  // 阿里云账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM用户进行API访问或日常运维,请登录RAM控制台创建RAM用户。
  accessKeyId: 'yourAccessKeyId',
  accessKeySecret: 'yourAccessKeySecret',
  // 填写Bucket名称,例如examplebucket。
  bucket: 'examplebucket',
});

async function abortMultipartUpload() {
  // 填写Object完整路径,例如exampledir/exampleobject.txt。Object完整路径中不能包含Bucket名称。
  const name = 'exampledir/exampleobject.txt';
  // 填写uploadId。uploadId来自于multipartUpload返回的结果。
  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',
  // 阿里云账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM用户进行API访问或日常运维,请登录RAM控制台创建RAM用户。
  accessKeyId: 'yourAccessKeyId',
  accessKeySecret: 'yourAccessKeySecret',
  // 填写Bucket名称,例如examplebucket。
  bucket: 'examplebucket',
});

async function listUploads(query = {}) {
  // query中支持设置prefix、marker、delimiter、upload-id-marker和max-uploads参数。
  const result = await client.listUploads(query);

  result.uploads.forEach(upload => {
    console.log(upload.uploadId); // 分片上传的uploadId。
    console.log(upload.name); // 将所有上传完成后的分片(Part)组合为一个完整的Object,并指定Object完整路径。
  });
}

const query = {
  'max-uploads': 1000, // 指定此次返回Multipart Uploads事件的最大个数。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',
      // 阿里云账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM用户进行API访问或日常运维,请登录RAM控制台创建RAM用户。
      accessKeyId: 'yourAccessKeyId',
      accessKeySecret: 'yourAccessKeySecret',
      // 填写Bucket名称,例如examplebucket。
      bucket: 'examplebucket',
    });
    
    async function listParts() {
      const query = {
        'max-parts': 1000, // 指定此次返回的最大分片(Part)个数。max-parts参数的默认值和最大值均为1000。
      };
      // 依次填写Object完整路径(例如exampledir/exampleobject.txt)和uploadId。Object完整路径中不能包含Bucket名称。
      const result = await client.listParts('exampledir/exampleobject.txt', '0004B999EF518A1FE585B0C9360D****', query);
    
      result.parts.forEach(part => {
        console.log(part.PartNumber);
        console.log(part.LastModified);
        console.log(part.ETag);
        console.log(part.Size);
      });
    }
    
    listParts();
  • 列举所有已上传的分片

    默认情况下,client.listParts只能一次列举1000个分片。当分片数量大于1000时,请使用以下代码列举所有已上传的分片。

    const OSS = require('ali-oss');
    
    const client = new OSS({
      // yourRegion填写Bucket所在地域。以华东1(杭州)为例,Region填写为oss-cn-hangzhou。
      region: 'yourRegion',
      // 阿里云账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM用户进行API访问或日常运维,请登录RAM控制台创建RAM用户。
      accessKeyId: 'yourAccessKeyId',
      accessKeySecret: 'yourAccessKeySecret',
      // 填写Bucket名称,例如examplebucket。
      bucket: 'examplebucket',
    });
    
    async function listParts() {
      const query = {
        'max-parts': 1000, // 指定此次返回的最大分片(Part)个数。max-parts参数的默认值和最大值均为1000。
      };
      let result;
      do {
        // 依次填写Object完整路径(例如exampledir/exampleobject.txt)和uploadId。Object完整路径中不能包含Bucket名称。
        result = await client.listParts('exampledir/exampleobject.txt', '0004B999EF518A1FE585B0C9360D****', query);
        // 指定列举分片的起始位置,例如2。只有分片号大于此参数值的分片会被列举。
        query['2'] = 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();