使用簡單上傳方式上傳超過 5 GB 的大檔案時,不僅耗時較長,而且容易因網路中斷或程式異常導致上傳失敗。您可以使用分區上傳方式,先將大檔案分成多個較小的片段(Part),然後並發上傳,提高上傳速度。此外,在某個Part上傳失敗時,您只需重新上傳該Part,避免整個檔案上傳失敗。
使用情境
大檔案加速上傳:當檔案大小超過 5 GB 時,使用分區上傳可實現並行上傳多個Part,從而加快上傳速度。
應對網路環境波動:在網路環境不佳的情況下,分區上傳尤為有利。如果上傳過程中出現失敗,您只需重傳失敗的部分,避免重新上傳整個檔案的麻煩,節省了時間和頻寬。
暫停和恢複上傳:分區上傳任務沒有到期時間。您可以隨時暫停和恢複分區上傳,直到完成或取消分區上傳。
檔案大小不確定:在某些情境中,例如視頻監控等行業應用中,檔案的最終大小可能不確定。使用分區上傳可以讓您在未知檔案大小的情況下開始上傳。
分區上傳流程
分區上傳(Multipart Upload)分為以下三個步驟:
|
操作方式
OSS控制台不支援分區上傳。如果您需要上傳超過5GB的檔案,請使用ossbrowser、SDK、ossutil。
使用命令列工具ossutil
使用命令列工具ossutil 2.0的進階命令cp(上傳檔案)上傳檔案時,如果檔案較大,ossutil會自動使用分區上傳方式。
ossutil cp D:/localpath/example.iso oss://examplebucket/desfolder/
如需手動實現分區上傳,您可以組合使用API級命令initiate-multipart-upload、upload-part、complete-multipart-upload。
使用阿里雲SDK
Java
使用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
推薦使用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
推薦使用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(®ion, "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
推薦使用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 // 完成分區上傳後的詳細結果
);
Node.js
使用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();
更多情境的程式碼範例請參見分區上傳。
C#
使用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);
}
更多情境的程式碼範例請參見分區上傳。
Browser.js
使用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>
更多情境的程式碼範例請參見分區上傳。
Android
使用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());
更多情境的程式碼範例請參見分區上傳。
C++
使用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;
}
更多情境的程式碼範例請參見分區上傳。
Object C
使用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];
更多情境的程式碼範例請參見分區上傳。
C
使用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 Policy或Bucket Policy授予操作許可權。
API | Action | 說明 |
InitiateMultipartUpload |
| 初始化分區上傳任務。 |
| 初始化分區上傳任務時,如果通過x-oss-tagging指定Object的標籤,則需要此操作的許可權。 | |
| 上傳Object時,如果Object的中繼資料套件含X-Oss-Server-Side-Encryption: KMS,則需要這兩個操作的許可權。 | |
|
API | Action | 說明 |
UploadPart |
| 上傳Part。 |
API | Action | 說明 |
UploadPartCopy |
| 從一個已存在的Object中拷貝資料來上傳一個Part時,需要讀取源Object的許可權。 |
| 從一個已存在的Object中拷貝資料來上傳一個Part時,需要寫入目標Object的許可權。 | |
| 從一個已存在的Object中拷貝資料來上傳一個Part時,如果通過versionId指定Object的版本,需要讀取源Object的指定版本的許可權。 |
API | Action | 說明 |
CompleteMultipartUpload |
| 將Part合并為Object。 |
| 將Part合并為Object時,如果通過x-oss-tagging指定Object的標籤,則需要此操作的許可權。 |
API | Action | 說明 |
AbortMultipartUpload |
| 取消MultipartUpload事件並刪除對應的Part資料。 |
API | Action | 說明 |
ListMultipartUploads |
| 列舉所有執行中的Multipart Upload事件,即已經初始化但尚未完成(Complete)或者尚未中止(Abort)的Multipart Upload事件。 |
API | Action | 說明 |
ListParts |
| 列舉指定Upload ID所屬的所有已經上傳成功的Part。 |
計費說明
通過分區上傳方式將檔案上傳到OSS,會產生以下計費項目。有關計費項目的定價詳情,請參見OSS產品定價。
API | 計費項目 | 說明 |
InitiateMultipartUpload | PUT 類型請求 | 根據成功的請求次數計算請求費用。 |
API | 計費項目 | 說明 |
UploadPart | PUT 類型請求 | 根據成功的請求次數計算請求費用。 |
儲存費用 | 根據Part的儲存類型、大小和時間長度收取儲存費用。Part的儲存類型與Object的儲存類型一致,但是Part無最小計量限制。即某個Part小於64 KB,仍然按照Part的實際大小計算。將Part上傳到OSS後,只要Part沒有被刪除或合并為Object,Part就會產生儲存費用,與Part是否被訪問,以及對Part執行何種操作無關。 |
API | 計費項目 | 說明 |
UploadPartCopy | PUT 類型請求 | 根據成功的請求次數計算請求費用。 |
API | 計費項目 | 說明 |
CompleteMultipartUpload | PUT 類型請求 | 根據成功的請求次數計算請求費用。 |
儲存費用 | 根據Object的儲存類型、大小和時間長度收取儲存費用。將OSS中的Part合并為Object後,只要Object儲存在OSS就會產生儲存費用,與Object是否被訪問,以及對Object執行何種操作無關。 |
API | 計費項目 | 說明 |
AbortMultipartUpload | PUT 類型請求 | 根據成功的請求次數計算請求費用。 重要
|
API | 計費項目 | 說明 |
ListMultipartUploads | PUT 類型請求 | 根據成功的請求次數計算請求費用。 |
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手動合并分區。