分片上传

更新时间:2025-04-18 02:18:46

使用简单上传方式上传超过 5 GB 的大文件时,不仅耗时较长,而且容易因网络中断或程序异常导致上传失败。您可以使用分片上传方式,先将大文件分成多个较小的碎片(Part),然后并发上传,提高上传速度。此外,在某个Part上传失败时,您只需重新上传该Part,避免整个文件上传失败。

使用场景

  • 大文件加速上传:当文件大小超过 5 GB 时,使用分片上传可实现并行上传多个Part,从而加快上传速度。

  • 应对网络环境波动:在网络环境不佳的情况下,分片上传尤为有利。如果上传过程中出现失败,您只需重传失败的部分,避免重新上传整个文件的麻烦,节省了时间和带宽。

  • 暂停和恢复上传:分片上传任务没有过期时间。您可以随时暂停和恢复分片上传,直到完成或取消分片上传。

  • 文件大小不确定:在某些场景中,例如视频监控等行业应用中,文件的最终大小可能不确定。使用分片上传可以让您在未知文件大小的情况下开始上传。

分片上传流程

image

分片上传(Multipart Upload)分为以下三个步骤:

  1. 调用InitiateMultipartUpload接口初始化分片上传任务。

  2. 调用UploadPart接口上传分片。

    • 要上传的文件切分成Part之后,文件顺序是通过上传过程中指定的partNumber来确定的,实际执行中并没有顺序要求,因此可以实现并发上传。并发数并非越多越快,请结合自身网络状况和设备负载综合考虑。

    • 默认情况下,已经上传但还没有调用CompleteMultipartUploadPart不会被自动删除,因此如果要终止上传并删除占用的存储空间,需要调用AbortMultipartUpload

  3. 调用CompleteMultipartUpload接口将Part合并为Object。

操作方式

说明

OSS控制台不支持分片上传。如果您需要上传超过5GB的文件,请使用ossbrowser、SDK、ossutil。

使用图形化管理工具ossbrowser

使用图形化管理工具ossbrowser 2.0(预览版)上传文件时,如果文件较大,ossbrowser会自动使用分片上传方式。

使用命令行工具ossutil

使用命令行工具ossutil 2.0的高级命令cp(上传文件)上传文件时,如果文件较大,ossutil会自动使用分片上传方式。

ossutil cp D:/localpath/example.iso oss://examplebucket/desfolder/

如需手动实现分片上传,您可以组合使用API级命令initiate-multipart-uploadupload-partcomplete-multipart-upload

使用阿里云SDK

Java
Python
Go
PHP
Node.js
C#
Browser.js
Android
C++
Object C
C

使用OSS Java SDK完成分片上传的示例代码如下。

import com.aliyun.oss.*;
import com.aliyun.oss.common.auth.*;
import com.aliyun.oss.common.comm.SignVersion;
import com.aliyun.oss.internal.Mimetypes;
import com.aliyun.oss.model.*;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

public class Demo {

    public static void main(String[] args) throws Exception {
        // Endpoint以华东1(杭州)为例,其它Region请按实际情况填写。
        String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
        // 从环境变量中获取访问凭证。运行本代码示例之前,请确保已设置环境变量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
        EnvironmentVariableCredentialsProvider credentialsProvider = CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();
        // 填写Bucket名称,例如examplebucket。
        String bucketName = "examplebucket";
        // 填写Object完整路径,例如exampledir/exampleobject.txt。Object完整路径中不能包含Bucket名称。
        String objectName = "exampledir/exampleobject.txt";
        // 待上传本地文件路径。
        String filePath = "D:\\localpath\\examplefile.txt";
        // 填写Bucket所在地域。以华东1(杭州)为例,Region填写为cn-hangzhou。
        String region = "cn-hangzhou";

        // 创建OSSClient实例。
        ClientBuilderConfiguration clientBuilderConfiguration = new ClientBuilderConfiguration();
        clientBuilderConfiguration.setSignatureVersion(SignVersion.V4);
        OSS ossClient = OSSClientBuilder.create()
                .endpoint(endpoint)
                .credentialsProvider(credentialsProvider)
                .clientConfiguration(clientBuilderConfiguration)
                .region(region)
                .build();

        try {
            // 创建InitiateMultipartUploadRequest对象。
            InitiateMultipartUploadRequest request = new InitiateMultipartUploadRequest(bucketName, objectName);

            // 创建ObjectMetadata并设置Content-Type。
            ObjectMetadata metadata = new ObjectMetadata();
            if (metadata.getContentType() == null) {
                metadata.setContentType(Mimetypes.getInstance().getMimetype(new File(filePath), objectName));
            }
            System.out.println("Content-Type: " + metadata.getContentType());

            // 将metadata绑定到上传请求中。
            request.setObjectMetadata(metadata);

            // 初始化分片。
            InitiateMultipartUploadResult upresult = ossClient.initiateMultipartUpload(request);
            // 返回uploadId。
            String uploadId = upresult.getUploadId();

            // partETags是PartETag的集合。PartETag由分片的ETag和分片号组成。
            List<PartETag> partETags = new ArrayList<PartETag>();
            // 每个分片的大小,用于计算文件有多少个分片。单位为字节。
            // 分片最小值为100 KB,最大值为5 GB。最后一个分片的大小允许小于100 KB。
            // 设置分片大小为 1 MB。
            final long partSize = 1 * 1024 * 1024L;   

            // 根据上传的数据大小计算分片数。以本地文件为例,说明如何通过File.length()获取上传数据的大小。
            final File sampleFile = new File(filePath);
            long fileLength = sampleFile.length();
            int partCount = (int) (fileLength / partSize);
            if (fileLength % partSize != 0) {
                partCount++;
            }
            // 遍历分片上传。
            for (int i = 0; i < partCount; i++) {
                long startPos = i * partSize;
                long curPartSize = (i + 1 == partCount) ? (fileLength - startPos) : partSize;
                UploadPartRequest uploadPartRequest = new UploadPartRequest();
                uploadPartRequest.setBucketName(bucketName);
                uploadPartRequest.setKey(objectName);
                uploadPartRequest.setUploadId(uploadId);
                // 设置上传的分片流。
                // 以本地文件为例说明如何创建FileInputStream,并通过InputStream.skip()方法跳过指定数据。
                InputStream instream = new FileInputStream(sampleFile);
                instream.skip(startPos);
                uploadPartRequest.setInputStream(instream);
                // 设置分片大小。
                uploadPartRequest.setPartSize(curPartSize);
                // 设置分片号。每一个上传的分片都有一个分片号,取值范围是1~10000,如果超出此范围,OSS将返回InvalidArgument错误码。
                uploadPartRequest.setPartNumber(i + 1);
                // 每个分片不需要按顺序上传,甚至可以在不同客户端上传,OSS会按照分片号排序组成完整的文件。
                UploadPartResult uploadPartResult = ossClient.uploadPart(uploadPartRequest);
                // 每次上传分片之后,OSS的返回结果包含PartETag。PartETag将被保存在partETags中。
                partETags.add(uploadPartResult.getPartETag());

                // 关闭流
                instream.close();
            }

            // 创建CompleteMultipartUploadRequest对象。
            // 在执行完成分片上传操作时,需要提供所有有效的partETags。OSS收到提交的partETags后,会逐一验证每个分片的有效性。当所有的数据分片验证通过后,OSS将把这些分片组合成一个完整的文件。
            CompleteMultipartUploadRequest completeMultipartUploadRequest =
                    new CompleteMultipartUploadRequest(bucketName, objectName, uploadId, partETags);

            // 完成分片上传。
            CompleteMultipartUploadResult completeMultipartUploadResult = ossClient.completeMultipartUpload(completeMultipartUploadRequest);
            System.out.println("上传成功,ETag:" + completeMultipartUploadResult.getETag());

        } catch (OSSException oe) {
            System.out.println("Caught an OSSException, which means your request made it to OSS, "
                    + "but was rejected with an error response for some reason.");
            System.out.println("Error Message:" + oe.getErrorMessage());
            System.out.println("Error Code:" + oe.getErrorCode());
            System.out.println("Request ID:" + oe.getRequestId());
            System.out.println("Host ID:" + oe.getHostId());
        } catch (ClientException ce) {
            System.out.println("Caught a ClientException, which means the client encountered "
                    + "a serious internal problem while trying to communicate with OSS, "
                    + "such as not being able to access the network.");
            System.out.println("Error Message:" + ce.getMessage());
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
    }
}

更多场景的示例代码请参见分片上传

推荐使用Python SDK V2分片上传的示例代码如下,详细请参见分片上传

如需使用Python SDK V1示例代码请参见分片上传

import os
import argparse
import alibabacloud_oss_v2 as oss

# 创建命令行参数解析器,用于分片上传示例。
parser = argparse.ArgumentParser(description="multipart upload sample")

# 添加命令行参数 --region,表示存储空间所在的区域,必需参数
parser.add_argument('--region', help='The region in which the bucket is located.', required=True)

# 添加命令行参数 --bucket,表示存储空间的名称,必需参数
parser.add_argument('--bucket', help='The name of the bucket.', required=True)

# 添加命令行参数 --endpoint,表示其他服务可用来访问OSS的域名,非必需参数
parser.add_argument('--endpoint', help='The domain names that other services can use to access OSS')

# 添加命令行参数 --key,表示对象的名称,必需参数
parser.add_argument('--key', help='The name of the object.', required=True)

# 添加命令行参数 --file_path,表示要上传的文件路径,必需参数
parser.add_argument('--file_path', help='The path of Upload file.', required=True)


def main():
    # 解析命令行参数
    args = parser.parse_args()

    # 从环境变量中加载凭证信息,用于身份验证
    credentials_provider = oss.credentials.EnvironmentVariableCredentialsProvider()

    # 使用SDK的默认配置,并设置凭证提供者
    cfg = oss.config.load_default()
    cfg.credentials_provider = credentials_provider

    # 设置配置中的区域信息
    cfg.region = args.region

    # 如果提供了endpoint参数,则设置配置中的endpoint
    if args.endpoint is not None:
        cfg.endpoint = args.endpoint

    # 使用配置好的信息创建OSS客户端
    client = oss.Client(cfg)

    # 初始化分片上传请求,获取upload_id用于后续分片上传
    result = client.initiate_multipart_upload(oss.InitiateMultipartUploadRequest(
        bucket=args.bucket,
        key=args.key,
    ))

    # 定义每个分片的大小为5MB
    part_size = 5 * 1024 * 1024

    # 获取要上传文件的总大小
    data_size = os.path.getsize(args.file_path)

    # 初始化分片编号,从1开始
    part_number = 1

    # 存储每个分片上传的结果
    upload_parts = []

    # 打开文件以二进制模式读取
    with open(args.file_path, 'rb') as f:
        # 遍历文件,按照part_size分片上传
        for start in range(0, data_size, part_size):
            n = part_size
            if start + n > data_size:  # 处理最后一个分片可能小于part_size的情况
                n = data_size - start

            # 创建SectionReader来读取文件的特定部分
            reader = oss.io_utils.SectionReader(oss.io_utils.ReadAtReader(f), start, n)

            # 上传分片
            up_result = client.upload_part(oss.UploadPartRequest(
                bucket=args.bucket,
                key=args.key,
                upload_id=result.upload_id,
                part_number=part_number,
                body=reader
            ))

            # 打印每个分片上传的结果信息
            print(f'status code: {up_result.status_code},'
                  f' request id: {up_result.request_id},'
                  f' part number: {part_number},'
                  f' content md5: {up_result.content_md5},'
                  f' etag: {up_result.etag},'
                  f' hash crc64: {up_result.hash_crc64},'
                  )

            # 将分片上传结果保存到列表中
            upload_parts.append(oss.UploadPart(part_number=part_number, etag=up_result.etag))

            # 增加分片编号
            part_number += 1

    # 对上传的分片按照分片编号排序
    parts = sorted(upload_parts, key=lambda p: p.part_number)

    # 发送完成分片上传请求,合并所有分片为一个完整的对象
    result = client.complete_multipart_upload(oss.CompleteMultipartUploadRequest(
        bucket=args.bucket,
        key=args.key,
        upload_id=result.upload_id,
        complete_multipart_upload=oss.CompleteMultipartUpload(
            parts=parts
        )
    ))

    # 下面的代码是另一种方式,通过服务器端列出并合并所有分片数据为一个完整的对象
    # 这种方法适用于当您不确定所有分片是否都已成功上传时
    # Merge fragmented data into a complete Object through the server-side List method
    # result = client.complete_multipart_upload(oss.CompleteMultipartUploadRequest(
    #     bucket=args.bucket,
    #     key=args.key,
    #     upload_id=result.upload_id,
    #     complete_all='yes'
    # ))

    # 输出完成分片上传的结果信息
    print(f'status code: {result.status_code},'
          f' request id: {result.request_id},'
          f' bucket: {result.bucket},'
          f' key: {result.key},'
          f' location: {result.location},'
          f' etag: {result.etag},'
          f' encoding type: {result.encoding_type},'
          f' hash crc64: {result.hash_crc64},'
          f' version id: {result.version_id},'
    )

if __name__ == "__main__":
    main()  # 脚本入口,当文件被直接运行时调用main函数

推荐使用Go SDK V2分片上传的示例代码如下,详细请参见分片上传

如需使用Go SDK V1分片上传的示例代码请参见分片上传

package main

import (
	"bufio"
	"bytes"
	"context"
	"flag"
	"io"
	"log"
	"os"
	"sync"

	"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss"
	"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss/credentials"
)

// 定义全局变量
var (
	region     string // 存储区域
	bucketName string // 源存储空间名称
	objectName string // 源对象名称

)

// init函数用于初始化命令行参数
func init() {
	flag.StringVar(&region, "region", "", "The region in which the bucket is located.")
	flag.StringVar(&bucketName, "bucket", "", "The name of the source bucket.")
	flag.StringVar(&objectName, "object", "", "The name of the source object.")
}

func main() {
	// 解析命令行参数
	flag.Parse()

	// 定义上传ID
	var uploadId string

	// 检查源存储空间名称是否为空
	if len(bucketName) == 0 {
		flag.PrintDefaults()
		log.Fatalf("invalid parameters, source bucket name required")
	}

	// 检查存储区域是否为空
	if len(region) == 0 {
		flag.PrintDefaults()
		log.Fatalf("invalid parameters, region required")
	}

	// 检查源对象名称是否为空
	if len(objectName) == 0 {
		flag.PrintDefaults()
		log.Fatalf("invalid parameters, source object name required")
	}

	// 加载默认配置并设置凭证提供者和区域
	cfg := oss.LoadDefaultConfig().
		WithCredentialsProvider(credentials.NewEnvironmentVariableCredentialsProvider()).
		WithRegion(region)

	// 创建OSS客户端
	client := oss.NewClient(cfg)

	// 初始化分片上传请求
	initRequest := &oss.InitiateMultipartUploadRequest{
		Bucket: oss.Ptr(bucketName),
		Key:    oss.Ptr(objectName),
	}
	initResult, err := client.InitiateMultipartUpload(context.TODO(), initRequest)
	if err != nil {
		log.Fatalf("failed to initiate multipart upload %v", err)
	}

	// 打印初始化分片上传的结果
	log.Printf("initiate multipart upload result:%#v\n", *initResult.UploadId)
	uploadId = *initResult.UploadId

	// 初始化等待组和互斥锁
	var wg sync.WaitGroup
	var parts []oss.UploadPart
	count := 3
	var mu sync.Mutex

	// 读取本地文件内容到内存,将yourLocalFile替换为实际的本地文件名和路径
	file, err := os.Open("yourLocalFile")
	if err != nil {
		log.Fatalf("failed to open local file %v", err)
	}
	defer file.Close()

	bufReader := bufio.NewReader(file)
	content, err := io.ReadAll(bufReader)
	if err != nil {
		log.Fatalf("failed to read local file %v", err)
	}
	log.Printf("file size: %d\n", len(content))

	// 计算每个分片的大小
	chunkSize := len(content) / count
	if chunkSize == 0 {
		chunkSize = 1
	}

	// 启动多个goroutine进行分片上传
	for i := 0; i < count; i++ {
		start := i * chunkSize
		end := start + chunkSize
		if i == count-1 {
			end = len(content)
		}

		wg.Add(1)
		go func(partNumber int, start, end int) {
			defer wg.Done()

			// 创建分片上传请求
			partRequest := &oss.UploadPartRequest{
				Bucket:     oss.Ptr(bucketName),                 // 目标存储空间名称
				Key:        oss.Ptr(objectName),                 // 目标对象名称
				PartNumber: int32(partNumber),                   // 分片编号
				UploadId:   oss.Ptr(uploadId),                   // 上传ID
				Body:       bytes.NewReader(content[start:end]), // 分片内容
			}

			// 发送分片上传请求
			partResult, err := client.UploadPart(context.TODO(), partRequest)
			if err != nil {
				log.Fatalf("failed to upload part %d: %v", partNumber, err)
			}

			// 记录分片上传结果
			part := oss.UploadPart{
				PartNumber: partRequest.PartNumber,
				ETag:       partResult.ETag,
			}

			// 使用互斥锁保护共享数据
			mu.Lock()
			parts = append(parts, part)
			mu.Unlock()
		}(i+1, start, end)
	}

	// 等待所有goroutine完成
	wg.Wait()

	// 完成分片上传请求
	request := &oss.CompleteMultipartUploadRequest{
		Bucket:   oss.Ptr(bucketName),
		Key:      oss.Ptr(objectName),
		UploadId: oss.Ptr(uploadId),
		CompleteMultipartUpload: &oss.CompleteMultipartUpload{
			Parts: parts,
		},
	}
	result, err := client.CompleteMultipartUpload(context.TODO(), request)
	if err != nil {
		log.Fatalf("failed to complete multipart upload %v", err)
	}

	// 打印完成分片上传的结果
	log.Printf("complete multipart upload result:%#v\n", result)
}

推荐使用PHP SDK V2分片上传的示例代码如下,详细请参见分片上传

如需使用PHP SDK V1分片上传的示例代码请参见分片上传

<?php

// 引入自动加载文件,确保依赖库能够正确加载
require_once __DIR__ . '/../vendor/autoload.php';

use AlibabaCloud\Oss\V2 as Oss;

// 定义命令行参数的描述信息
$optsdesc = [
    "region" => ['help' => 'The region in which the bucket is located.', 'required' => True], // Bucket所在的地域(必填)
    "endpoint" => ['help' => 'The domain names that other services can use to access OSS.', 'required' => False], // 访问域名(可选)
    "bucket" => ['help' => 'The name of the bucket', 'required' => True], // Bucket名称(必填)
    "key" => ['help' => 'The name of the object', 'required' => True], // 对象名称(必填)
];

// 将参数描述转换为getopt所需的长选项格式
// 每个参数后面加上":"表示该参数需要值
$longopts = \array_map(function ($key) {
    return "$key:";
}, array_keys($optsdesc));

// 解析命令行参数
$options = getopt("", $longopts);

// 验证必填参数是否存在
foreach ($optsdesc as $key => $value) {
    if ($value['required'] === True && empty($options[$key])) {
        $help = $value['help']; // 获取参数的帮助信息
        echo "Error: the following arguments are required: --$key, $help" . PHP_EOL;
        exit(1); // 如果必填参数缺失,则退出程序
    }
}

// 从解析的参数中提取值
$region = $options["region"]; // Bucket所在的地域
$bucket = $options["bucket"]; // Bucket名称
$key = $options["key"];       // 对象名称

// 加载环境变量中的凭证信息
// 使用EnvironmentVariableCredentialsProvider从环境变量中读取Access Key ID和Access Key Secret
$credentialsProvider = new Oss\Credentials\EnvironmentVariableCredentialsProvider();

// 使用SDK的默认配置
$cfg = Oss\Config::loadDefault();
$cfg->setCredentialsProvider($credentialsProvider); // 设置凭证提供者
$cfg->setRegion($region); // 设置Bucket所在的地域
$cfg->setEndpoint('http://oss-cn-hangzhou.aliyuncs.com'); // 设置访问域名

// 创建OSS客户端实例
$client = new Oss\Client($cfg);

// 初始化分片上传任务
$initResult = $client->initiateMultipartUpload(
    new Oss\Models\InitiateMultipartUploadRequest(
        bucket: $bucket,
        key: $key
    )
);

// 定义大文件路径和分片大小
$bigFileName = "/Users/yourLocalPath/yourFileName"; // 填写大文件路径
$partSize = 5 * 1024 * 1024; // 设置分片大小,单位为字节(此处设置为1MB)
$fileSize = filesize($bigFileName); // 获取文件大小
$partsNum = intdiv($fileSize, $partSize) + intval(1); // 计算分片数量
$parts = []; // 用于存储每个分片的上传结果

$i = 1; // 分片编号从1开始
$file = new \GuzzleHttp\Psr7\LazyOpenStream($bigFileName, 'rb'); // 打开文件流
while ($i <= $partsNum) {
    // 上传单个分片
    $partResult = $client->uploadPart(
        new Oss\Models\UploadPartRequest(
            bucket: $bucket,
            key: $key,
            partNumber: $i, // 当前分片编号
            uploadId: $initResult->uploadId, // 初始上传任务返回的uploadId
            contentLength: null, // 可选:分片内容长度
            contentMd5: null, // 可选:分片内容的MD5校验值
            trafficLimit: null, // 可选:流量限制
            requestPayer: null, // 可选:请求支付方
            body: new \GuzzleHttp\Psr7\LimitStream($file, $partSize, ($i - 1) * $partSize) // 读取当前分片的数据
        )
    );

    // 保存分片上传结果
    $part = new Oss\Models\UploadPart(
        partNumber: $i, // 分片编号
        etag: $partResult->etag // 分片上传后返回的ETag值
    );

    array_push($parts, $part); // 将当前分片的上传结果保存到分片列表中
    $i++; // 递增分片编号,准备处理下一个分片
}

// 完成分片上传任务
$comResult = $client->completeMultipartUpload(
    new Oss\Models\CompleteMultipartUploadRequest(
        bucket: $bucket,
        key: $key,
        uploadId: $initResult->uploadId, // 初始上传任务返回的uploadId
        acl: null, // 可选:设置对象的访问控制列表(ACL)
        completeMultipartUpload: new Oss\Models\CompleteMultipartUpload(
            parts: $parts // 提交所有分片的上传结果
        )
    )
);

// 打印完成分片上传的结果
printf(
    'status code:' . $comResult->statusCode . PHP_EOL . // HTTP状态码,例如200表示成功
    'request id:' . $comResult->requestId . PHP_EOL .   // 请求ID,用于调试或追踪请求
    'complete multipart upload result:' . var_export($comResult, true) . PHP_EOL // 完成分片上传后的详细结果
);

使用OSS Node.js SDK分片上传的示例代码如下。

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,
  authorizationV4: true,
  // 填写存储空间名称。
  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();

更多场景的代码示例请参见分片上传

使用OSS C# SDK分片上传的示例代码如下。

using Aliyun.OSS;
using Aliyun.OSS.Common;

// yourEndpoint填写Bucket所在地域对应的Endpoint。以华东1(杭州)为例,Endpoint填写为https://oss-cn-hangzhou.aliyuncs.com。
var endpoint = "yourEndpoint";
// 从环境变量中获取访问凭证。运行本代码示例之前,请确保已设置环境变量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
var accessKeyId = Environment.GetEnvironmentVariable("OSS_ACCESS_KEY_ID");
var accessKeySecret = Environment.GetEnvironmentVariable("OSS_ACCESS_KEY_SECRET");
// 填写Bucket名称。
var bucketName = "examplebucket";
// 填写Object完整路径。Object完整路径中不能包含Bucket名称。
var objectName = "exampleobject.txt";
// 填写本地文件的完整路径。如果未指定本地路径,则默认从示例程序所属项目对应本地路径中上传文件。
var localFilename = "D:\\localpath\\examplefile.txt";
// 填写Bucket所在地域对应的Region。以华东1(杭州)为例,Region填写为cn-hangzhou。
const string region = "cn-hangzhou";

// 创建ClientConfiguration实例,按照您的需要修改默认参数。
var conf = new ClientConfiguration();

// 设置v4签名。
conf.SignatureVersion = SignatureVersion.V4;

// 创建OssClient实例。
var client = new OssClient(endpoint, accessKeyId, accessKeySecret, conf);
client.SetRegion(region);

// 初始化分片上传,返回uploadId。
var uploadId = "";
try
{
    // 定义上传的文件及所属Bucket的名称。您可以在InitiateMultipartUploadRequest中设置ObjectMeta,但不必指定其中的ContentLength。
    var request = new InitiateMultipartUploadRequest(bucketName, objectName);
    var result = client.InitiateMultipartUpload(request);
    uploadId = result.UploadId;
    // 打印UploadId。
    Console.WriteLine("Init multi part upload succeeded");
    // 根据uploadId执行取消分片上传事件或者列举已上传分片的操作。
    // 如果您需要根据您需要uploadId执行取消分片上传事件的操作,您需要在调用InitiateMultipartUpload完成初始化分片之后获取uploadId。 
    // 如果您需要根据您需要uploadId执行列举已上传分片的操作,您需要在调用InitiateMultipartUpload完成初始化分片之后,且在调用CompleteMultipartUpload完成分片上传之前获取uploadId。
    Console.WriteLine("Upload Id:{0}", result.UploadId);
}
catch (Exception ex)
{
    Console.WriteLine("Init multi part upload failed, {0}", ex.Message);
    Environment.Exit(1);
}
// 计算分片总数。
// 分片最小值为100 KB,最大值为5 GB。最后一个分片的大小允许小于100 KB。
var partSize = 100 * 1024;
var fi = new FileInfo(localFilename);
var fileSize = fi.Length;
var partCount = fileSize / partSize;
if (fileSize % partSize != 0)
{
    partCount++;
}
//  当初始化分片成功时,开始分片上传。PartETags是保存PartETag的列表,OSS收到用户提交的分片列表后,会逐一验证每个分片数据的有效性。当所有的数据分片通过验证后,OSS会将这些分片组合成一个完整的文件。
var partETags = new List<PartETag>();
try
{
    using (var fs = File.Open(localFilename, FileMode.Open))
    {
        for (var i = 0; i < partCount; i++)
        {
            var skipBytes = (long)partSize * i;
            // 定位到本次上传的起始位置。
            fs.Seek(skipBytes, 0);
            // 计算本次上传的分片大小,最后一片为剩余的数据大小。
            var size = (partSize < fileSize - skipBytes) ? partSize : (fileSize - skipBytes);
            var request = new UploadPartRequest(bucketName, objectName, uploadId)
            {
                InputStream = fs,
                PartSize = size,
                PartNumber = i + 1
            };
            // 调用UploadPart接口执行上传功能,返回结果中包含了这个数据片的ETag值。
            var result = client.UploadPart(request);
            partETags.Add(result.PartETag);
            Console.WriteLine("finish {0}/{1}", partETags.Count, partCount);
        }
        Console.WriteLine("Put multi part upload succeeded");
    }
}
catch (Exception ex)
{
    Console.WriteLine("Put multi part upload failed, {0}", ex.Message);
    Environment.Exit(1);
}
// 当分片上传成功后,合并分片。
try
{
    var completeMultipartUploadRequest = new CompleteMultipartUploadRequest(bucketName, objectName, uploadId);
    foreach (var partETag in partETags)
    {
        completeMultipartUploadRequest.PartETags.Add(partETag);
    }
    var result = client.CompleteMultipartUpload(completeMultipartUploadRequest);
    Console.WriteLine("complete multi part succeeded");
}
catch (Exception ex)
{
    Console.WriteLine("complete multi part failed, {0}", ex.Message);
    Environment.Exit(1);
}

更多场景的代码示例请参见分片上传

使用OSS Browser.js SDK分片上传的示例代码如下。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Document</title>
  </head>

  <body>
    <button id="submit">上传</button>
    <input id="file" type="file" />
    <!--导入sdk文件-->
    <script
      type="text/javascript"
      src="https://gosspublic.alicdn.com/aliyun-oss-sdk-6.18.0.min.js"
    ></script>
    <script type="text/javascript">
      const client = new OSS({
        // yourRegion填写Bucket所在地域。以华东1(杭州)为例,Region填写为oss-cn-hangzhou。
        region: "yourRegion",
        authorizationV4: true,
        // 从STS服务获取的临时访问密钥(AccessKey ID和AccessKey Secret)。
        accessKeyId: "yourAccessKeyId",
        accessKeySecret: "yourAccessKeySecret",
        // 从STS服务获取的安全令牌(SecurityToken)。
        stsToken: "yourSecurityToken",
        // 填写Bucket名称,例如examplebucket。
        bucket: "examplebucket",
      });

      const headers = {
        // 指定该Object被下载时的网页缓存行为。
        "Cache-Control": "no-cache",
        // 指定该Object被下载时的名称。
        "Content-Disposition": "example.txt",
        // 指定过期时间,单位为毫秒。
        Expires: "1000",
        // 指定Object的存储类型。
        "x-oss-storage-class": "Standard",
        // 指定Object标签,可同时设置多个标签。
        "x-oss-tagging": "Tag1=1&Tag2=2",
        // 指定初始化分片上传时是否覆盖同名Object。此处设置为true,表示禁止覆盖同名Object。
        "x-oss-forbid-overwrite": "true",
      };

      // 指定上传到examplebucket的Object名称,例如exampleobject.txt。
      const name = "exampleobject.txt";
      // 获取DOM。
      const submit = document.getElementById("submit");
      const options = {
        // 获取分片上传进度、断点和返回值。
        progress: (p, cpt, res) => {
          console.log(p);
        },
        // 设置并发上传的分片数量。
        parallel: 4,
        // 设置分片大小。默认值为1 MB,最小值为100 KB,最大值为5 GB。最后一个分片的大小允许小于100 KB。
        partSize: 1024 * 1024,
        // headers,
        // 自定义元数据,通过HeadObject接口可以获取Object的元数据。
        meta: { year: 2020, people: "test" },
        mime: "text/plain",
      };

      // 监听按钮。
      submit.addEventListener("click", async () => {
        try {
          const data = document.getElementById("file").files[0];
          // 分片上传。
          const res = await client.multipartUpload(name, data, {
            ...options,
            // 设置上传回调。
            // 如果不涉及回调服务器,请删除callback相关设置。
            callback: {
              // 设置回调请求的服务器地址。
              url: "http://examplebucket.aliyuncs.com:23450",
              // 设置回调请求消息头中Host的值,即您的服务器配置Host的值。
              host: "yourHost",
              /* eslint no-template-curly-in-string: [0] */
              // 设置发起回调时请求body的值。
              body: "bucket=${bucket}&object=${object}&var1=${x:var1}",
              // 设置发起回调请求的Content-Type。
              contentType: "application/x-www-form-urlencoded",
              customValue: {
                // 设置发起回调请求的自定义参数。
                var1: "value1",
                var2: "value2",
              },
            },
          });
          console.log(res);
        } catch (err) {
          console.log(err);
        }
      });
    </script>
  </body>
</html>

更多场景的代码示例请参见分片上传

使用OSS Android SDK分片上传的示例代码如下。

// 填写Bucket名称,例如examplebucket。
String bucketName = "examplebucket";
// 填写Object完整路径,例如exampledir/exampleobject.txt。Object完整路径中不能包含Bucket名称。
String objectName = "exampledir/exampleobject.txt";
// 填写本地文件完整路径,例如/storage/emulated/0/oss/examplefile.txt。
String localFilepath = "/storage/emulated/0/oss/examplefile.txt";

// 初始化分片上传。
InitiateMultipartUploadRequest init = new InitiateMultipartUploadRequest(bucketName, objectName);
InitiateMultipartUploadResult initResult = oss.initMultipartUpload(init);
// 返回uploadId。
String uploadId = initResult.getUploadId();
// 根据uploadId执行取消分片上传事件或者列举已上传分片的操作。
// 如果您需要根据您需要uploadId执行取消分片上传事件的操作,您需要在调用InitiateMultipartUpload完成初始化分片之后获取uploadId。 
// 如果您需要根据您需要uploadId执行列举已上传分片的操作,您需要在调用InitiateMultipartUpload完成初始化分片之后,且在调用CompleteMultipartUpload完成分片上传之前获取uploadId。
// Log.d("uploadId", uploadId);

// 设置单个Part的大小,单位为字节。分片最小值为100 KB,最大值为5 GB。最后一个分片的大小允许小于100 KB。
int partCount = 100 * 1024;
// 分片上传。
List<PartETag> partETags = new ArrayList<>();
for (int i = 1; i < 5; i++) {
    byte[] data = new byte[partCount];

    RandomAccessFile raf = new RandomAccessFile(localFilepath, "r");
    long skip = (i-1) * partCount;
    raf.seek(skip);
    raf.readFully(data, 0, partCount);

    UploadPartRequest uploadPart = new UploadPartRequest();
    uploadPart.setBucketName(bucketName);
    uploadPart.setObjectKey(objectName);
    uploadPart.setUploadId(uploadId);
    // 设置分片号,从1开始标识。每一个上传的Part都有一个分片号,取值范围是1~10000。
    uploadPart.setPartNumber(i); 
    uploadPart.setPartContent(data);
    try {
        UploadPartResult result = oss.uploadPart(uploadPart);
        PartETag partETag = new PartETag(uploadPart.getPartNumber(), result.getETag());
        partETags.add(partETag);
    } catch (ServiceException serviceException) {
        OSSLog.logError(serviceException.getErrorCode());
    }
}
Collections.sort(partETags, new Comparator<PartETag>() {
    @Override
    public int compare(PartETag lhs, PartETag rhs) {
        if (lhs.getPartNumber() < rhs.getPartNumber()) {
            return -1;
        } else if (lhs.getPartNumber() > rhs.getPartNumber()) {
            return 1;
        } else {
            return 0;
        }
    }
});

// 完成分片上传。
CompleteMultipartUploadRequest complete = new CompleteMultipartUploadRequest(bucketName, objectName, uploadId, partETags);

// 上传回调。完成分片上传请求时可以设置CALLBACK_SERVER参数,请求完成后会向指定的Server Address发送回调请求。可通过返回结果的completeResult.getServerCallbackReturnBody()查看servercallback结果。
complete.setCallbackParam(new HashMap<String, String>() {
    {
        put("callbackUrl", CALLBACK_SERVER); //修改为您的服务器地址。
        put("callbackBody", "test");
    }
});
CompleteMultipartUploadResult completeResult = oss.completeMultipartUpload(complete);
OSSLog.logError("-------------- serverCallback: " + completeResult.getServerCallbackReturnBody());

更多场景的代码示例请参见分片上传

使用OSS C++ SDK分片上传的示例代码如下。

#include <alibabacloud/oss/OssClient.h>
#include <fstream>

int64_t getFileSize(const std::string& file)
{
    std::fstream f(file, std::ios::in | std::ios::binary);
    f.seekg(0, f.end);
    int64_t size = f.tellg();
    f.close();
    return size;
}

using namespace AlibabaCloud::OSS;

int main(void)
{
    /* 初始化OSS账号信息 */
    
    /* yourEndpoint填写Bucket所在地域对应的Endpoint。以华东1(杭州)为例,Endpoint填写为https://oss-cn-hangzhou.aliyuncs.com。*/
    std::string Endpoint = "yourEndpoint";
    /* yourRegion填写Bucket所在地域对应的Region。以华东1(杭州)为例,Region填写为cn-hangzhou。*/
    std::string Region = "yourRegion";
    /* 填写Bucket名称,例如examplebucket */
    std::string BucketName = "examplebucket";
    /* 填写Object完整路径,完整路径中不能包含Bucket名称,例如exampledir/exampleobject.txt。 */
    std::string ObjectName = "exampledir/exampleobject.txt";

    /* 初始化网络等资源 */
    InitializeSdk();

    ClientConfiguration conf;
    conf.signatureVersion = SignatureVersionType::V4;
    /* 从环境变量中获取访问凭证。运行本代码示例之前,请确保已设置环境变量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。*/
    auto credentialsProvider = std::make_shared<EnvironmentVariableCredentialsProvider>();
    OssClient client(Endpoint, credentialsProvider, conf);
    client.SetRegion(Region);
  
    InitiateMultipartUploadRequest initUploadRequest(BucketName, ObjectName);
    /*(可选)请参见如下示例设置存储类型 */
    //initUploadRequest.MetaData().addHeader("x-oss-storage-class", "Standard");

    /* 初始化分片上传事件 */
    auto uploadIdResult = client.InitiateMultipartUpload(initUploadRequest);
    /* 根据UploadId执行取消分片上传事件或者列举已上传分片的操作。*/
    /* 如果您需要根据您需要UploadId执行取消分片上传事件的操作,您需要在调用InitiateMultipartUpload完成初始化分片之后获取uploadId。*/
    /* 如果您需要根据您需要UploadId执行列举已上传分片的操作,您需要在调用InitiateMultipartUpload完成初始化分片之后,且在调用CompleteMultipartUpload完成分片上传之前获取uploadId。*/ 
    auto uploadId = uploadIdResult.result().UploadId();
    std::string fileToUpload = "yourLocalFilename";
    /* 分片最小值为100 KB,最大值为5 GB。最后一个分片的大小允许小于100 KB。*/
    int64_t partSize = 100 * 1024;
    PartList partETagList;
    auto fileSize = getFileSize(fileToUpload);
    int partCount = static_cast<int>(fileSize / partSize);
    /* 计算分片个数 */
    if (fileSize % partSize != 0) {
        partCount++;
    }

    /* 对每一个分片进行上传 */
    for (int i = 1; i <= partCount; i++) {
        auto skipBytes = partSize * (i - 1);
        auto size = (partSize < fileSize - skipBytes) ? partSize : (fileSize - skipBytes);
        std::shared_ptr<std::iostream> content = std::make_shared<std::fstream>(fileToUpload, std::ios::in|std::ios::binary);
        content->seekg(skipBytes, std::ios::beg);

        UploadPartRequest uploadPartRequest(BucketName, ObjectName, content);
        uploadPartRequest.setContentLength(size);
        uploadPartRequest.setUploadId(uploadId);
        uploadPartRequest.setPartNumber(i);
        auto uploadPartOutcome = client.UploadPart(uploadPartRequest);
        if (uploadPartOutcome.isSuccess()) {
            Part part(i, uploadPartOutcome.result().ETag());
            partETagList.push_back(part);
        }
        else {
            std::cout << "uploadPart fail" <<
            ",code:" << uploadPartOutcome.error().Code() <<
            ",message:" << uploadPartOutcome.error().Message() <<
            ",requestId:" << uploadPartOutcome.error().RequestId() << std::endl;
        }

    }

    /* 完成分片上传 */
    /* 在执行完成分片上传操作时,需要提供所有有效的partETags。OSS收到提交的partETags后,会逐一验证每个分片的有效性。当所有的数据分片验证通过后,OSS将把这些分片组合成一个完整的文件。*/
    CompleteMultipartUploadRequest request(BucketName, ObjectName);
    request.setUploadId(uploadId);
    request.setPartList(partETagList);
    /*(可选)请参见如下示例设置读写权限ACL */
    //request.setAcl(CannedAccessControlList::Private);

    auto outcome = client.CompleteMultipartUpload(request);

    if (!outcome.isSuccess()) {
        /* 异常处理 */
        std::cout << "CompleteMultipartUpload fail" <<
        ",code:" << outcome.error().Code() <<
        ",message:" << outcome.error().Message() <<
        ",requestId:" << outcome.error().RequestId() << std::endl;
        return -1;
    }

    /* 释放网络等资源 */
    ShutdownSdk();
    return 0;
}

更多场景的代码示例请参见分片上传

使用OSS Object C SDK分片上传的示例代码如下。

__block NSString * uploadId = nil;
__block NSMutableArray * partInfos = [NSMutableArray new];
// 填写Bucket名称,例如examplebucket。
NSString * uploadToBucket = @"examplebucket";
// 填写Object完整路径,例如exampledir/exampleobject.txt。Object完整路径中不能包含Bucket名称。
NSString * uploadObjectkey = @"exampledir/exampleobject.txt";
// OSSInitMultipartUploadRequest用于指定上传文件的名称以及上传文件所属的存储空间的名称。
OSSInitMultipartUploadRequest * init = [OSSInitMultipartUploadRequest new];
init.bucketName = uploadToBucket;
init.objectKey = uploadObjectkey;
// init.contentType = @"application/octet-stream";
// multipartUploadInit返回的结果中包含UploadId,UploadId是分片上传的唯一标识。
OSSTask * initTask = [client multipartUploadInit:init];
[initTask waitUntilFinished];
if (!initTask.error) {
    OSSInitMultipartUploadResult * result = initTask.result;
    uploadId = result.uploadId;
    // 根据uploadId执行取消分片上传事件或者列举已上传分片的操作。
    // 如果您需要根据您需要uploadId执行取消分片上传事件的操作,您需要在调用InitiateMultipartUpload完成初始化分片之后获取uploadId。
    // 如果您需要根据您需要uploadId执行列举已上传分片的操作,您需要在调用InitiateMultipartUpload完成初始化分片之后,且在调用CompleteMultipartUpload完成分片上传之前获取uploadId。
    //NSLog(@"UploadId": %@, uploadId);
} else {
    NSLog(@"multipart upload failed, error: %@", initTask.error);
    return;
}

// 指定要上传的文件。
NSString * filePath = @"<filepath>";
// 获取文件大小。
uint64_t fileSize = [[[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:nil] fileSize];
// 设置分片上传数量。
int chuckCount = 10;
// 设置分片大小。
uint64_t offset = fileSize/chuckCount;
for (int i = 1; i <= chuckCount; i++) {
    OSSUploadPartRequest * uploadPart = [OSSUploadPartRequest new];
    uploadPart.bucketName = uploadToBucket;
    uploadPart.objectkey = uploadObjectkey;
    uploadPart.uploadId = uploadId;
    uploadPart.partNumber = i; // part number start from 1

    NSFileHandle* readHandle = [NSFileHandle fileHandleForReadingAtPath:filePath];
    [readHandle seekToFileOffset:offset * (i -1)];

    NSData* data = [readHandle readDataOfLength:offset];
    uploadPart.uploadPartData = data;

    OSSTask * uploadPartTask = [client uploadPart:uploadPart];

    [uploadPartTask waitUntilFinished];

    if (!uploadPartTask.error) {
        OSSUploadPartResult * result = uploadPartTask.result;
        uint64_t fileSize = [[[NSFileManager defaultManager] attributesOfItemAtPath:uploadPart.uploadPartFileURL.absoluteString error:nil] fileSize];
        [partInfos addObject:[OSSPartInfo partInfoWithPartNum:i eTag:result.eTag size:fileSize]];
    } else {
        NSLog(@"upload part error: %@", uploadPartTask.error);
        return;
    }
}
OSSCompleteMultipartUploadRequest * complete = [OSSCompleteMultipartUploadRequest new];
complete.bucketName = uploadToBucket;
complete.objectKey = uploadObjectkey;
complete.uploadId = uploadId;
complete.partInfos = partInfos;

OSSTask * completeTask = [client completeMultipartUpload:complete];

[[completeTask continueWithBlock:^id(OSSTask *task) {
    if (!task.error) {
        OSSCompleteMultipartUploadResult * result = task.result;
        // ...
    } else {
        // ...
    }
    return nil;
}] waitUntilFinished];

更多场景的代码示例请参见分片上传

使用OSS C SDK分片上传的示例代码如下。

#include "oss_api.h"
#include "aos_http_io.h"
#include <sys/stat.h>
/* yourEndpoint填写Bucket所在地域对应的Endpoint。以华东1(杭州)为例,Endpoint填写为https://oss-cn-hangzhou.aliyuncs.com。*/
const char *endpoint = "yourEndpoint";

/* 填写Bucket名称,例如examplebucket。*/
const char *bucket_name = "examplebucket";
/* 填写Object完整路径,完整路径中不能包含Bucket名称,例如exampledir/exampleobject.txt。*/
const char *object_name = "exampledir/exampleobject.txt";
/* 填写本地文件的完整路径。*/
const char *local_filename = "yourLocalFilename";
/* yourRegion填写Bucket所在地域对应的Region。以华东1(杭州)为例,Region填写为cn-hangzhou。*/
const char *region = "yourRegion";
void init_options(oss_request_options_t *options)
{
    options->config = oss_config_create(options->pool);
    /* 用char*类型的字符串初始化aos_string_t类型。*/
    aos_str_set(&options->config->endpoint, endpoint);
    /* 从环境变量中获取访问凭证。运行本代码示例之前,请确保已设置环境变量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。*/    
    aos_str_set(&options->config->access_key_id, getenv("OSS_ACCESS_KEY_ID"));
    aos_str_set(&options->config->access_key_secret, getenv("OSS_ACCESS_KEY_SECRET"));
    //需要额外配置以下两个参数
    aos_str_set(&options->config->region, region);
    options->config->signature_version = 4;
    /* 是否使用CNAME访问OSS服务。0表示不使用。*/
    options->config->is_cname = 0;
    /* 设置网络相关参数,比如超时时间等。*/
    options->ctl = aos_http_controller_create(options->pool, 0);
}
int64_t get_file_size(const char *file_path)
{
    int64_t filesize = -1;
    struct stat statbuff;
    if(stat(file_path, &statbuff) < 0){
        return filesize;
    } else {
        filesize = statbuff.st_size;
    }
    return filesize;
}
int main(int argc, char *argv[])
{
    /* 在程序入口调用aos_http_io_initialize方法来初始化网络、内存等全局资源。*/
    if (aos_http_io_initialize(NULL, 0) != AOSE_OK) {
        exit(1);
    }
    /* 用于内存管理的内存池(pool),等价于apr_pool_t。其实现代码在apr库中。*/
    aos_pool_t *pool;
    /* 重新创建一个内存池,第二个参数是NULL,表示没有继承其它内存池。*/
    aos_pool_create(&pool, NULL);
    /* 创建并初始化options,该参数包括endpoint、access_key_id、acces_key_secret、is_cname、curl等全局配置信息。*/
    oss_request_options_t *oss_client_options;
    /* 在内存池中分配内存给options。*/
    oss_client_options = oss_request_options_create(pool);
    /* 初始化Client的选项oss_client_options。*/
    init_options(oss_client_options);
    /* 初始化参数。*/
    aos_string_t bucket;
    aos_string_t object;
    oss_upload_file_t *upload_file = NULL;
    aos_string_t upload_id;   
    aos_table_t *headers = NULL;
    aos_table_t *complete_headers = NULL;
    aos_table_t *resp_headers = NULL;
    aos_status_t *resp_status = NULL; 
    aos_str_set(&bucket, bucket_name);
    aos_str_set(&object, object_name);
    aos_str_null(&upload_id);
    headers = aos_table_make(pool, 1);
    complete_headers = aos_table_make(pool, 1);
    int part_num = 1;
    /* 初始化分片上传,获取一个上传ID(upload_id)。*/
    resp_status = oss_init_multipart_upload(oss_client_options, &bucket, &object, &upload_id, headers, &resp_headers);
    /* 判断分片上传初始化是否成功。*/
    if (aos_status_is_ok(resp_status)) {
        printf("Init multipart upload succeeded, upload_id:%.*s\n", 
               upload_id.len, upload_id.data);
    } else {
        printf("Init multipart upload failed, upload_id:%.*s\n", 
               upload_id.len, upload_id.data);
    }
    /* 上传分片。*/
    int64_t file_length = 0;
    int64_t pos = 0;
    aos_list_t complete_part_list;
       oss_complete_part_content_t* complete_content = NULL;
    char* part_num_str = NULL;
    char* etag = NULL;
    aos_list_init(&complete_part_list);
    file_length = get_file_size(local_filename);
    while(pos < file_length) {
        upload_file = oss_create_upload_file(pool);
        aos_str_set(&upload_file->filename, local_filename);
        upload_file->file_pos = pos;
        pos += 100 * 1024;
        upload_file->file_last = pos < file_length ? pos : file_length;
        resp_status = oss_upload_part_from_file(oss_client_options, &bucket, &object, &upload_id, part_num++, upload_file, &resp_headers);

        /* 保存分片号和ETag。*/
        complete_content = oss_create_complete_part_content(pool);
        part_num_str = apr_psprintf(pool, "%d", part_num-1);
        aos_str_set(&complete_content->part_number, part_num_str);
        etag = apr_pstrdup(pool,
        (char*)apr_table_get(resp_headers, "ETag"));
        aos_str_set(&complete_content->etag, etag);
        aos_list_add_tail(&complete_content->node, &complete_part_list);

        if (aos_status_is_ok(resp_status)) {
            printf("Multipart upload part from file succeeded\n");
        } else {
            printf("Multipart upload part from file failed\n");
        }
    }

    /* 完成分片上传。*/
    resp_status = oss_complete_multipart_upload(oss_client_options, &bucket, &object, &upload_id,
            &complete_part_list, complete_headers, &resp_headers);
    /* 判断分片上传是否完成。*/
    if (aos_status_is_ok(resp_status)) {
        printf("Complete multipart upload from file succeeded, upload_id:%.*s\n", 
               upload_id.len, upload_id.data);
    } else {
        printf("Complete multipart upload from file failed\n");
    }
    /* 释放内存池,相当于释放了请求过程中各资源分配的内存。*/
    aos_pool_destroy(pool);
    /* 释放之前分配的全局资源。*/
    aos_http_io_deinitialize();
    return 0;
}

更多场景的代码示例请参见分片上传

相关API

以上操作方式底层基于API实现,如果您的程序自定义要求较高,您可以直接发起REST API请求。直接发起REST API请求需要手动编写代码计算签名。

  • 关于初始化分片上传任务的接口说明,请参见InitiateMultipartUpload

  • 关于上传Part的接口说明,请参见UploadPart

  • 关于从一个已存在的Object中拷贝数据来上传一个Part的接口说明,请参见UploadPartCopy

  • 关于完成分片上传任务的接口说明,请参见CompleteMultipartUpload

  • 关于取消MultipartUpload事件并删除对应的Part的接口说明,请参见AbortMultipartUpload

  • 关于列举所有执行中的Multipart Upload事件的接口说明,请参见ListMultipartUploads

  • 关于列举所有已经上传成功的Part的接口说明,请参见ListParts

权限说明

阿里云账号默认拥有全部权限。阿里云账号下的RAM用户或RAM角色默认没有任何权限,需要阿里云账号或账号管理员通过RAM PolicyBucket Policy授予操作权限。

API

Action

说明

API

Action

说明

InitiateMultipartUpload

oss:PutObject

初始化分片上传任务。

oss:PutObjectTagging

初始化分片上传任务时,如果通过x-oss-tagging指定Object的标签,则需要此操作的权限。

kms:GenerateDataKey

上传Object时,如果Object的元数据包含X-Oss-Server-Side-Encryption: KMS,则需要这两个操作的权限。

kms:Decrypt

API

Action

说明

API

Action

说明

UploadPart

oss:PutObject

上传Part。

API

Action

说明

API

Action

说明

UploadPartCopy

oss:GetObject

从一个已存在的Object中拷贝数据来上传一个Part时,需要读取源Object的权限。

oss:PutObject

从一个已存在的Object中拷贝数据来上传一个Part时,需要写入目标Object的权限。

oss:GetObjectVersion

从一个已存在的Object中拷贝数据来上传一个Part时,如果通过versionId指定Object的版本,需要读取源Object的指定版本的权限。

API

Action

说明

API

Action

说明

CompleteMultipartUpload

oss:PutObject

Part合并为Object。

oss:PutObjectTagging

Part合并为Object时,如果通过x-oss-tagging指定Object的标签,则需要此操作的权限。

API

Action

说明

API

Action

说明

AbortMultipartUpload

oss:AbortMultipartUpload

取消MultipartUpload事件并删除对应的Part数据。

API

Action

说明

API

Action

说明

ListMultipartUploads

oss:ListMultipartUploads

列举所有执行中的Multipart Upload事件,即已经初始化但尚未完成(Complete)或者尚未中止(Abort)的Multipart Upload事件。

API

Action

说明

API

Action

说明

ListParts

oss:ListParts

列举指定Upload ID所属的所有已经上传成功的Part。

计费说明

通过分片上传方式将文件上传到OSS,会产生以下计费项。有关计费项的定价详情,请参见OSS产品定价

API

计费项

说明

API

计费项

说明

InitiateMultipartUpload

PUT 类型请求

根据成功的请求次数计算请求费用。

API

计费项

说明

API

计费项

说明

UploadPart

PUT 类型请求

根据成功的请求次数计算请求费用。

存储费用

根据Part的存储类型、大小和时长收取存储费用。Part的存储类型与Object的存储类型一致,但是Part无最小计量限制。即某个Part小于64 KB,仍然按照Part的实际大小计算。将Part上传到OSS后,只要Part没有被删除或合并为Object,Part就会产生存储费用,与Part是否被访问,以及对Part执行何种操作无关。

API

计费项

说明

API

计费项

说明

UploadPartCopy

PUT 类型请求

根据成功的请求次数计算请求费用。

API

计费项

说明

API

计费项

说明

CompleteMultipartUpload

PUT 类型请求

根据成功的请求次数计算请求费用。

存储费用

根据Object的存储类型、大小和时长收取存储费用。将OSS中的Part合并为Object后,只要Object存储在OSS就会产生存储费用,与Object是否被访问,以及对Object执行何种操作无关。

API

计费项

说明

API

计费项

说明

AbortMultipartUpload

PUT 类型请求

根据成功的请求次数计算请求费用。

重要
  • 在中国内地各地域,通过生命周期规则删除低频访问、归档、冷归档类型碎片的Put类请求费用高于删除标准存储类型碎片的Put类请求费用。通过生命周期规则删除深度冷归档存储类型碎片,不收取Put类请求费用。

  • 在中国香港以及海外地域,通过生命周期规则删除各存储类型碎片时不收取Put类请求费用。

API

计费项

说明

API

计费项

说明

ListMultipartUploads

PUT 类型请求

根据成功的请求次数计算请求费用。

API

计费项

说明

API

计费项

说明

ListParts

PUT 类型请求

根据成功的请求次数计算请求费用。

使用限制

限制项

说明

限制项

说明

单个文件的大小

不超过48.8TB

Part数量

1~10,000

单个Part大小

最小值为100 KB,最大值为5 GB。最后一个Part的大小允许小于100 KB。

单次ListParts请求返回的Part最大数量

1,000

单次ListMultipartUploads请求返回的Multipart Upload事件最大数量

1,000

常见问题

是否支持分片上传目录?

不支持。

如何优化大量文件的上传性能?

如果您在上传大量文件时,在命名上使用了顺序前缀(如时间戳或字母顺序),可能会出现大量文件索引集中存储于存储空间中某个特定分区的情况。此时如果您的请求速率过大,会导致请求速率下降。建议您在上传大量文件时,不要使用顺序前缀的文件名。更多信息,请参见OSS性能最佳实践

如何避免高额的深度冷归档类型的PUT类请求费用?

如果要上传的文件数量较多,直接指定上传的文件类型为深度冷归档类型会造成较高的PUT类请求费用。建议您先将文件的存储类型指定为标准存储进行上传,然后通过生命周期规则将其转储为深度冷归档类型,从而降低PUT类请求费用。

如何防止文件被意外覆盖?

上传同名文件会覆盖OSS中已有文件。您可以通过以下方式防止文件被意外覆盖:

  • 开启版本控制功能:开启版本控制功能后,被覆盖的文件会以历史版本的形式保存下来,您可以随时恢复历史版本文件。更多信息,请参见版本控制介绍

  • 在上传请求中携带禁止覆盖同名文件的参数 :在上传请求的header中携带x-oss-forbid-overwrite参数,并指定其值为true。当您上传的文件在OSS中存在同名文件时,该文件会上传失败,并返回FileAlreadyExists错误。更多信息,请参见InitiateMultipartUpload

如何删除碎片?

分片上传过程被中断后,如果没有调用AbortMultipartUpload,已上传的Part会一直保存在Bucket中。如果您不再需要这些Part,请通过以下方式删除,以免产生额外的存储费用。

如何列举分片?

  • 如果您希望列举指定Upload ID所属的所有已经上传成功Part,请参见ListParts

  • 如果您希望列举已经初始化但还未完成(Complete)或者还未中止(Abort)的Multipart Upload事件,请参见ListMultipartUploads

分片上传是否支持上传本地已加密的压缩文件?

支持。

分片上传过程中断后,重新上传时是否会覆盖已上传Part?

分片上传过程中断后,如果使用同一个Upload ID重新上传所有Part,则会覆盖之前上传的同名Part;如果使用新的Upload ID重新上传所有Part,旧的Upload ID中的分片会作为碎片继续保留。

分片上传时Upload ID是什么含义?

Upload ID用于唯一标识分片上传事件。对于同一个Upload ID,分片号(PartNumber)用于标识该分片在整个文件内的相对位置。

分片上传时Upload ID有效期多久?

Upload ID在分片上传过程中一直有效,如果上传终止或上传完成,Upload ID将失效。如果需要再次分片上传,您需要重新初始化生成一个新的Upload ID。

OSS是否支持自动合并分片?

OSS不支持自动合并分片,您需要通过调用CompleteMultipartUpload手动合并分片。

  • 本页导读 (1)
  • 使用场景
  • 分片上传流程
  • 操作方式
  • 使用图形化管理工具ossbrowser
  • 使用命令行工具ossutil
  • 使用阿里云SDK
  • 相关API
  • 权限说明
  • 计费说明
  • 使用限制
  • 常见问题
  • 是否支持分片上传目录?
  • 如何优化大量文件的上传性能?
  • 如何避免高额的深度冷归档类型的PUT类请求费用?
  • 如何防止文件被意外覆盖?
  • 如何删除碎片?
  • 如何列举分片?
  • 分片上传是否支持上传本地已加密的压缩文件?
  • 分片上传过程中断后,重新上传时是否会覆盖已上传Part?
  • 分片上传时Upload ID是什么含义?
  • 分片上传时Upload ID有效期多久?
  • OSS是否支持自动合并分片?
AI助理

点击开启售前

在线咨询服务

你好,我是AI助理

可以解答问题、推荐解决方案等