在上傳大文件(超過5 GB)到OSS的過程中,如果出現網絡中斷、程序異常退出等問題導致文件上傳失敗,您需要使用分片上傳的方式上傳大文件。分片上傳通過將待上傳的大文件分成多個較小的碎片(Part),充分利用網絡帶寬和服務器資源并發上傳多個Part,加快上傳完成時間,并在Part上傳完成之后調用CompleteMultipartUpload接口將這些Part組合成一個完整的Object。
前提條件
已創建存儲空間(Bucket)。具體操作,請參見創建存儲空間。
要分片上傳,您必須有
oss:PutObject
權限。具體操作,請參見RAM Policy常見示例。
使用場景
大文件加速上傳
當文件大小超過5 GB時,使用分片上傳可實現并行上傳多個Part以加快上傳速度。
網絡環境較差
網絡環境較差時,建議使用分片上傳。當出現上傳失敗的時候,您僅需重傳失敗的Part。
文件大小不確定
可以在需要上傳的文件大小還不確定的情況下開始上傳,這種場景在視頻監控等行業應用中比較常見。
分片上傳流程
流程說明如下:
如果您需要完成分片上傳的完整流程,包括初始化分片上傳,上傳分片以及將分片合為完成的Object,您需要oss:PutObject
權限。具體操作,請參見為RAM用戶授權自定義的權限策略。
將待上傳文件按照一定大小進行分片。
使用InitiateMultipartUpload接口初始化一個分片上傳任務。
使用UploadPart接口上傳分片。
文件切分成Part之后,文件順序是通過上傳過程中指定的
partNumber
來確定,所以您可以并發上傳這些碎片。并發數并非越多越快,請結合自身網絡狀況和設備負載綜合考慮。如果您希望終止上傳任務,可調用AbortMultipartUpload接口,成功上傳的Part會一并刪除。
使用CompleteMultipartUpload接口將Part組合成一個Object。
使用限制
限制項 | 規格 |
文件大小 | 不超過48.8 TB |
Part數量 | 1~10, 000個 |
單個Part大小 | 最小值為100 KB,最大值為5 GB。最后一個Part的大小允許小于100 KB。 |
單次ListParts請求返回的Part最大數量 | 1, 000個 |
單次ListMultipartUploads請求返回的Multipart Upload事件最大數量 | 1, 000個 |
注意事項
通過分片上傳的方式上傳文件時,一次只能上傳一個文件,且不支持上傳目錄。
降低PUT類請求費用
如果要上傳的文件數量較多,直接指定上傳的文件類型為深度冷歸檔類型會造成較高的PUT類請求費用。建議您先將文件的存儲類型指定為標準存儲進行上傳,然后通過生命周期規則將其轉儲為深度冷歸檔類型,從而降低PUT類請求費用。
文件上傳性能調優
如果您在上傳大量文件時,在命名上使用了順序前綴(如時間戳或字母順序),可能會出現大量文件索引集中存儲于存儲空間中某個特定分區的情況。此時如果您的請求速率過大,會導致請求速率下降。建議您在上傳大量文件時,不要使用順序前綴的文件名。更多信息,請參見OSS性能最佳實踐。
文件覆蓋
上傳同名文件會覆蓋OSS中已有文件。您可以通過以下方式防止文件被意外覆蓋:
開啟版本控制功能
開啟版本控制功能后,被覆蓋的文件會以歷史版本的形式保存下來,您可以隨時恢復歷史版本文件。更多信息,請參見版本控制介紹。
在上傳請求中攜帶禁止覆蓋同名文件的參數
在上傳請求的header中攜帶x-oss-forbid-overwrite參數,并指定其值為
true
。當您上傳的文件在OSS中存在同名文件時,該文件會上傳失敗,并返回FileAlreadyExists
錯誤。更多信息,請參見InitiateMultipartUpload。
操作步驟
使用阿里云SDK
以下僅列舉常見SDK的分片上傳的代碼示例。關于其他SDK的分片上傳的代碼示例,請參見SDK簡介。
import com.aliyun.oss.ClientException;
import com.aliyun.oss.OSS;
import com.aliyun.oss.common.auth.*;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.OSSException;
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 metadata = new ObjectMetadata();
// metadata.setHeader(OSSHeaders.OSS_STORAGE_CLASS, StorageClass.Standard.toString());
// 指定該Object的網頁緩存行為。
// metadata.setCacheControl("no-cache");
// 指定該Object被下載時的名稱。
// metadata.setContentDisposition("attachment;filename=oss_MultipartUpload.txt");
// 指定該Object的內容編碼格式。
// metadata.setContentEncoding(OSSConstants.DEFAULT_CHARSET_NAME);
// 指定初始化分片上傳時是否覆蓋同名Object。此處設置為true,表示禁止覆蓋同名Object。
// metadata.setHeader("x-oss-forbid-overwrite", "true");
// 指定上傳該Object的每個part時使用的服務器端加密方式。
// metadata.setHeader(OSSHeaders.OSS_SERVER_SIDE_ENCRYPTION, ObjectMetadata.KMS_SERVER_SIDE_ENCRYPTION);
// 指定Object的加密算法。如果未指定此選項,表明Object使用AES256加密算法。
// metadata.setHeader(OSSHeaders.OSS_SERVER_SIDE_DATA_ENCRYPTION, ObjectMetadata.KMS_SERVER_SIDE_ENCRYPTION);
// 指定KMS托管的用戶主密鑰。
// metadata.setHeader(OSSHeaders.OSS_SERVER_SIDE_ENCRYPTION_KEY_ID, "9468da86-3509-4f8d-a61e-6eab1eac****");
// 指定Object的存儲類型。
// metadata.setHeader(OSSHeaders.OSS_STORAGE_CLASS, StorageClass.Standard);
// 指定Object的對象標簽,可同時設置多個標簽。
// metadata.setHeader(OSSHeaders.OSS_TAGGING, "a:1");
// request.setObjectMetadata(metadata);
// 根據文件自動設置ContentType。如果不設置,ContentType默認值為application/oct-srream。
if (metadata.getContentType() == null) {
metadata.setContentType(Mimetypes.getInstance().getMimetype(new File(filePath), objectName));
}
// 初始化分片。
InitiateMultipartUploadResult upresult = ossClient.initiateMultipartUpload(request);
// 返回uploadId。
String uploadId = upresult.getUploadId();
// 根據uploadId執行取消分片上傳事件或者列舉已上傳分片的操作。
// 如果您需要根據uploadId執行取消分片上傳事件的操作,您需要在調用InitiateMultipartUpload完成初始化分片之后獲取uploadId。
// 如果您需要根據uploadId執行列舉已上傳分片的操作,您需要在調用InitiateMultipartUpload完成初始化分片之后,且在調用CompleteMultipartUpload完成分片上傳之前獲取uploadId。
// System.out.println(uploadId);
// partETags是PartETag的集合。PartETag由分片的ETag和分片號組成。
List<PartETag> partETags = new ArrayList<PartETag>();
// 每個分片的大小,用于計算文件有多少個分片。單位為字節。
final long partSize = 1 * 1024 * 1024L; //1 MB。
// 根據上傳的數據大小計算分片數。以本地文件為例,說明如何通過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);
// 設置分片大小。除了最后一個分片沒有大小限制,其他的分片最小為100 KB。
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());
}
// 創建CompleteMultipartUploadRequest對象。
// 在執行完成分片上傳操作時,需要提供所有有效的partETags。OSS收到提交的partETags后,會逐一驗證每個分片的有效性。當所有的數據分片驗證通過后,OSS將把這些分片組合成一個完整的文件。
CompleteMultipartUploadRequest completeMultipartUploadRequest =
new CompleteMultipartUploadRequest(bucketName, objectName, uploadId, partETags);
// 如果需要在完成分片上傳的同時設置文件訪問權限,請參考以下示例代碼。
// completeMultipartUploadRequest.setObjectACL(CannedAccessControlList.Private);
// 指定是否列舉當前UploadId已上傳的所有Part。僅在Java SDK為3.14.0及以上版本時,支持通過服務端List分片數據來合并完整文件時,將CompleteMultipartUploadRequest中的partETags設置為null。
// Map<String, String> headers = new HashMap<String, String>();
// 如果指定了x-oss-complete-all:yes,則OSS會列舉當前UploadId已上傳的所有Part,然后按照PartNumber的序號排序并執行CompleteMultipartUpload操作。
// 如果指定了x-oss-complete-all:yes,則不允許繼續指定body,否則報錯。
// headers.put("x-oss-complete-all","yes");
// completeMultipartUploadRequest.setHeaders(headers);
// 完成分片上傳。
CompleteMultipartUploadResult completeMultipartUploadResult = ossClient.completeMultipartUpload(completeMultipartUploadRequest);
System.out.println(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 an 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();
}
}
}
}
<?php
if (is_file(__DIR__ . '/../autoload.php')) {
require_once __DIR__ . '/../autoload.php';
}
if (is_file(__DIR__ . '/../vendor/autoload.php')) {
require_once __DIR__ . '/../vendor/autoload.php';
}
use OSS\Credentials\EnvironmentVariableCredentialsProvider;
use OSS\OssClient;
use OSS\CoreOssException;
use OSS\Core\OssUtil;
// 從環境變量中獲取訪問憑證。運行本代碼示例之前,請確保已設置環境變量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
$provider = new EnvironmentVariableCredentialsProvider();
// Endpoint以華東1(杭州)為例,其它Region請按實際情況填寫。
$endpoint = 'https://oss-cn-hangzhou.aliyuncs.com';
// 填寫Bucket名稱,例如examplebucket。
$bucket= 'examplebucket';
//填寫不包含Bucket名稱在內的Object完整路徑,例如exampledir/exampleobject.txt。
$object = 'exampledir/exampleobject.txt';
// 填寫本地文件的完整路徑。
$uploadFile = 'D:\\localpath\\examplefile.txt';
$initOptions = array(
OssClient::OSS_HEADERS => array(
// 指定該Object被下載時的網頁緩存行為。
// 'Cache-Control' => 'no-cache',
// 指定該Object被下載時的名稱。
// 'Content-Disposition' => 'attachment;filename=oss_download.jpg',
// 指定該Object被下載時的內容編碼格式。
// 'Content-Encoding' => 'utf-8',
// 指定過期時間,單位為毫秒。
// 'Expires' => 150,
// 指定初始化分片上傳時是否覆蓋同名Object。此處設置為true,表示禁止覆蓋同名Object。
//'x-oss-forbid-overwrite' => 'true',
// 指定上傳該Object的每個part時使用的服務器端加密方式。
// 'x-oss-server-side-encryption'=> 'KMS',
// 指定Object的加密算法。
// 'x-oss-server-side-data-encryption'=>'SM4',
// 指定KMS托管的用戶主密鑰。
//'x-oss-server-side-encryption-key-id' => '9468da86-3509-4f8d-a61e-6eab1eac****',
// 指定Object的存儲類型。
// 'x-oss-storage-class' => 'Standard',
// 指定Object的對象標簽,可同時設置多個標簽。
// 'x-oss-tagging' => 'TagA=A&TagB=B',
),
);
/**
* 步驟1:初始化一個分片上傳事件,并獲取uploadId。
*/
try{
$config = array(
"provider" => $provider,
"endpoint" => $endpoint,
"signatureVersion" => OssClient::OSS_SIGNATURE_VERSION_V4,
"region"=> "cn-hangzhou"
);
$ossClient = new OssClient($config);
//返回uploadId。uploadId是分片上傳事件的唯一標識,您可以根據uploadId發起相關的操作,如取消分片上傳、查詢分片上傳等。
$uploadId = $ossClient->initiateMultipartUpload($bucket, $object, $initOptions);
print("initiateMultipartUpload OK" . "\n");
// 根據uploadId執行取消分片上傳事件或者列舉已上傳分片的操作。
// 如果您需要根據您需要uploadId執行取消分片上傳事件的操作,您需要在調用InitiateMultipartUpload完成初始化分片之后獲取uploadId。
// 如果您需要根據您需要uploadId執行列舉已上傳分片的操作,您需要在調用InitiateMultipartUpload完成初始化分片之后,且在調用CompleteMultipartUpload完成分片上傳之前獲取uploadId。
//print("UploadId: " . $uploadId . "\n");
} catch(OssException $e) {
printf($e->getMessage() . "\n");
return;
}
/*
* 步驟2:上傳分片。
*/
$partSize = 10 * 1024 * 1024;
$uploadFileSize = sprintf('%u',filesize($uploadFile));
$pieces = $ossClient->generateMultiuploadParts($uploadFileSize, $partSize);
$responseUploadPart = array();
$uploadPosition = 0;
$isCheckMd5 = true;
foreach ($pieces as $i => $piece) {
$fromPos = $uploadPosition + (integer)$piece[$ossClient::OSS_SEEK_TO];
$toPos = (integer)$piece[$ossClient::OSS_LENGTH] + $fromPos - 1;
$upOptions = array(
// 上傳文件。
$ossClient::OSS_FILE_UPLOAD => $uploadFile,
// 設置分片號。
$ossClient::OSS_PART_NUM => ($i + 1),
// 指定分片上傳起始位置。
$ossClient::OSS_SEEK_TO => $fromPos,
// 指定文件長度。
$ossClient::OSS_LENGTH => $toPos - $fromPos + 1,
// 是否開啟MD5校驗,true為開啟。
$ossClient::OSS_CHECK_MD5 => $isCheckMd5,
);
// 開啟MD5校驗。
if ($isCheckMd5) {
$contentMd5 = OssUtil::getMd5SumForFile($uploadFile, $fromPos, $toPos);
$upOptions[$ossClient::OSS_CONTENT_MD5] = $contentMd5;
}
try {
// 上傳分片。
$responseUploadPart[] = $ossClient->uploadPart($bucket, $object, $uploadId, $upOptions);
printf("initiateMultipartUpload, uploadPart - part#{$i} OK\n");
} catch(OssException $e) {
printf("initiateMultipartUpload, uploadPart - part#{$i} FAILED\n");
printf($e->getMessage() . "\n");
return;
}
}
// $uploadParts是由每個分片的ETag和分片號(PartNumber)組成的數組。
$uploadParts = array();
foreach ($responseUploadPart as $i => $eTag) {
$uploadParts[] = array(
'PartNumber' => ($i + 1),
'ETag' => $eTag,
);
}
/**
* 步驟3:完成上傳。
*/
$comOptions['headers'] = array(
// 指定完成分片上傳時是否覆蓋同名Object。此處設置為true,表示禁止覆蓋同名Object。
// 'x-oss-forbid-overwrite' => 'true',
// 如果指定了x-oss-complete-all:yes,則OSS會列舉當前uploadId已上傳的所有Part,然后按照PartNumber的序號排序并執行CompleteMultipartUpload操作。
// 'x-oss-complete-all'=> 'yes'
);
try {
// 執行completeMultipartUpload操作時,需要提供所有有效的$uploadParts。OSS收到提交的$uploadParts后,會逐一驗證每個分片的有效性。當所有的數據分片驗證通過后,OSS將把這些分片組合成一個完整的文件。
$ossClient->completeMultipartUpload($bucket, $object, $uploadId, $uploadParts,$comOptions);
printf( "Complete Multipart Upload OK\n");
} catch(OssException $e) {
printf("Complete Multipart Upload FAILED\n");
printf($e->getMessage() . "\n");
return;
}
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();
# -*- coding: utf-8 -*-
import os
from oss2 import SizedFileAdapter, determine_part_size
from oss2.models import PartInfo
import oss2
from oss2.credentials import EnvironmentVariableCredentialsProvider
# 從環境變量中獲取訪問憑證。運行本代碼示例之前,請確保已設置環境變量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
auth = oss2.ProviderAuthV4(EnvironmentVariableCredentialsProvider())
# 填寫Bucket所在地域對應的Endpoint。以華東1(杭州)為例,Endpoint填寫為https://oss-cn-hangzhou.aliyuncs.com。
endpoint = "https://oss-cn-hangzhou.aliyuncs.com"
# 填寫Endpoint對應的Region信息,例如cn-hangzhou。注意,v4簽名下,必須填寫該參數
region = "cn-hangzhou"
# yourBucketName填寫存儲空間名稱。
bucket = oss2.Bucket(auth, endpoint, "yourBucketName", region=region)
# 填寫不能包含Bucket名稱在內的Object完整路徑,例如exampledir/exampleobject.txt。
key = 'exampledir/exampleobject.txt'
# 填寫本地文件的完整路徑,例如D:\\localpath\\examplefile.txt。
filename = 'D:\\localpath\\examplefile.txt'
total_size = os.path.getsize(filename)
# determine_part_size方法用于確定分片大小。
part_size = determine_part_size(total_size, preferred_size=100 * 1024)
# 初始化分片。
# 如需在初始化分片時設置文件存儲類型,請在init_multipart_upload中設置相關Headers,參考如下。
# headers = dict()
# 指定該Object的網頁緩存行為。
# headers['Cache-Control'] = 'no-cache'
# 指定該Object被下載時的名稱。
# headers['Content-Disposition'] = 'oss_MultipartUpload.txt'
# 指定該Object的內容編碼格式。
# headers['Content-Encoding'] = 'utf-8'
# 指定過期時間,單位為毫秒。
# headers['Expires'] = '1000'
# 指定初始化分片上傳時是否覆蓋同名Object。此處設置為true,表示禁止覆蓋同名Object。
# headers['x-oss-forbid-overwrite'] = 'true'
# 指定上傳該Object的每個Part時使用的服務器端加密方式。
# headers[OSS_SERVER_SIDE_ENCRYPTION] = SERVER_SIDE_ENCRYPTION_KMS
# 指定Object的加密算法。如果未指定此選項,表明Object使用AES256加密算法。
# headers[OSS_SERVER_SIDE_DATA_ENCRYPTION] = SERVER_SIDE_ENCRYPTION_KMS
# 表示KMS托管的用戶主密鑰。
# headers[OSS_SERVER_SIDE_ENCRYPTION_KEY_ID] = '9468da86-3509-4f8d-a61e-6eab1eac****'
# 指定Object的存儲類型。
# headers['x-oss-storage-class'] = oss2.BUCKET_STORAGE_CLASS_STANDARD
# 指定Object的對象標簽,可同時設置多個標簽。
# headers[OSS_OBJECT_TAGGING] = 'k1=v1&k2=v2&k3=v3'
# upload_id = bucket.init_multipart_upload(key, headers=headers).upload_id
upload_id = bucket.init_multipart_upload(key).upload_id
# 根據upload_id執行取消分片上傳事件或者列舉已上傳分片的操作。
# 如果您需要根據您需要uploadId執行取消分片上傳事件的操作,您需要在調用InitiateMultipartUpload完成初始化分片之后獲取uploadId。
# 如果您需要根據您需要uploadId執行列舉已上傳分片的操作,您需要在調用InitiateMultipartUpload完成初始化分片之后,且在調用CompleteMultipartUpload完成分片上傳之前獲取uploadId。
# print("UploadID:", upload_id)
parts = []
# 逐個上傳分片。
with open(filename, 'rb') as fileobj:
part_number = 1
offset = 0
while offset < total_size:
num_to_upload = min(part_size, total_size - offset)
# 調用SizedFileAdapter(fileobj, size)方法會生成一個新的文件對象,重新計算起始追加位置。
result = bucket.upload_part(key, upload_id, part_number,
SizedFileAdapter(fileobj, num_to_upload))
parts.append(PartInfo(part_number, result.etag))
offset += num_to_upload
part_number += 1
# 完成分片上傳。
# 如需在完成分片上傳時設置相關Headers,請參考如下示例代碼。
headers = dict()
# 設置文件訪問權限ACL。此處設置為OBJECT_ACL_PRIVATE,表示私有權限。
# headers["x-oss-object-acl"] = oss2.OBJECT_ACL_PRIVATE
bucket.complete_multipart_upload(key, upload_id, parts, headers=headers)
# bucket.complete_multipart_upload(key, upload_id, parts)
<!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",
// 從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",
// 指定該Object被下載時的內容編碼格式。
"Content-Encoding": "utf-8",
// 指定過期時間,單位為毫秒。
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。
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>
using Aliyun.OSS;
// 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";
// 創建OssClient實例。
var client = new OssClient(endpoint, accessKeyId, accessKeySecret);
// 初始化分片上傳,返回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);
}
// 計算分片總數。
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);
}
// 填寫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。
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());
package main
import (
"fmt"
"log"
"os"
"github.com/aliyun/aliyun-oss-go-sdk/oss"
)
func main() {
// 從環境變量中獲取訪問憑證。運行本代碼示例之前,請確保已設置環境變量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
provider, err := oss.NewEnvironmentVariableCredentialsProvider()
if err != nil {
log.Fatalf("Error: %v", err)
}
// 創建OSSClient實例。
// yourEndpoint填寫Bucket對應的Endpoint,以華東1(杭州)為例,填寫為https://oss-cn-hangzhou.aliyuncs.com。其它Region請按實際情況填寫。
// yourRegion填寫Bucket所在地域,以華東1(杭州)為例,填寫為cn-hangzhou。其它Region請按實際情況填寫。
clientOptions := []oss.ClientOption{oss.SetCredentialsProvider(&provider)}
clientOptions = append(clientOptions, oss.Region("yourRegion"))
// 設置簽名版本
clientOptions = append(clientOptions, oss.AuthVersion(oss.AuthV4))
client, err := oss.New("yourEndpoint", "", "", clientOptions...)
if err != nil {
log.Fatalf("Error: %v", err)
}
// 設置存儲空間名稱
bucketName := "examplebucket"
// 設置Object完整路徑。Object完整路徑中不能包含Bucket名稱。
objectName := "exampleobject.txt"
// 設置本地文件的完整路徑。如果未指定本地路徑,則默認從示例程序所屬項目對應本地路徑中上傳文件。
localFilename := "/localpath/exampleobject.txt"
bucket, err := client.Bucket(bucketName)
if err != nil {
log.Fatalf("Error: %v", err)
}
// 設置分片大小(單位:字節),指定5MB為分片大小。
partSize := int64(5 * 1024 * 1024)
// 調用分片上傳函數。
if err := uploadMultipart(bucket, objectName, localFilename, partSize); err != nil {
log.Fatalf("Failed to upload multipart: %v", err)
}
}
// 分片上傳函數
func uploadMultipart(bucket *oss.Bucket, objectName, localFilename string, partSize int64) error {
// 將本地文件分片
chunks, err := oss.SplitFileByPartSize(localFilename, partSize)
if err != nil {
return fmt.Errorf("failed to split file into chunks: %w", err)
}
// 打開本地文件。
file, err := os.Open(localFilename)
if err != nil {
return fmt.Errorf("failed to open file: %w", err)
}
defer file.Close()
// 步驟1:初始化一個分片上傳事件。
imur, err := bucket.InitiateMultipartUpload(objectName)
if err != nil {
return fmt.Errorf("failed to initiate multipart upload: %w", err)
}
// 步驟2:上傳分片。
var parts []oss.UploadPart
for _, chunk := range chunks {
part, err := bucket.UploadPart(imur, file, chunk.Size, chunk.Number)
if err != nil {
// 如果上傳某個部分失敗,嘗試取消整個上傳任務。
if abortErr := bucket.AbortMultipartUpload(imur); abortErr != nil {
log.Printf("Failed to abort multipart upload: %v", abortErr)
}
return fmt.Errorf("failed to upload part: %w", err)
}
parts = append(parts, part)
}
// 指定Object的讀寫權限為私有,默認為繼承Bucket的讀寫權限。
objectAcl := oss.ObjectACL(oss.ACLPrivate)
// 步驟3:完成分片上傳。
_, err = bucket.CompleteMultipartUpload(imur, parts, objectAcl)
if err != nil {
// 如果完成上傳失敗,嘗試取消上傳。
if abortErr := bucket.AbortMultipartUpload(imur); abortErr != nil {
log.Printf("Failed to abort multipart upload: %v", abortErr)
}
return fmt.Errorf("failed to complete multipart upload: %w", err)
}
log.Printf("Multipart upload completed successfully.")
return nil
}
__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];
#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";
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;
}
#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";
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"));
/* 是否使用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;
}
上面所展示的代碼為逐個分片上傳,您也可使用并行上傳,以提高上傳速度,以下為并行上傳的代碼示例:
import os
from oss2 import determine_part_size, SizedFileAdapter
from oss2.models import PartInfo
import oss2
from concurrent.futures import ThreadPoolExecutor
from oss2.credentials import EnvironmentVariableCredentialsProvider
# 從環境變量中獲取訪問憑證。運行本代碼示例之前,請確保已設置環境變量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
auth = oss2.ProviderAuthV4(EnvironmentVariableCredentialsProvider())
# 填寫Bucket所在地域對應的Endpoint。以華東1(杭州)為例,Endpoint填寫為https://oss-cn-hangzhou.aliyuncs.com。
endpoint = "https://oss-cn-hangzhou.aliyuncs.com"
# 填寫Endpoint對應的Region信息,例如cn-hangzhou。注意,v4簽名下,必須填寫該參數
region = "cn-hangzhou"
# yourBucketName填寫存儲空間名稱。
bucket = oss2.Bucket(auth, endpoint, "yourbucketname", region=region)
# 填寫不能包含Bucket名稱在內的Object完整路徑,例如exampledir/exampleobject.txt。
key = 'exampledir/exampleobject.txt'
# 填寫本地文件的完整路徑(待上傳文件)
filename = 'D:\\localpath\\examplefile.txt'
def upload_part(filename, bucket, key, upload_id, part_number, offset, num_to_upload):
"""
執行單個分片的上傳操作
:param filename: 本地文件路徑
:param bucket: oss2.Bucket 對象,表示OSS存儲空間
:param key: 上傳文件的Object名稱
:param upload_id: 分片上傳的唯一標識符
:param part_number: 當前分片的編號
:param offset: 當前分片的起始位置
:param num_to_upload: 當前分片的大小
:return: PartInfo 對象,包含分片的ETag和編號
"""
with open(filename, 'rb') as fileobj:
fileobj.seek(offset)
result = bucket.upload_part(
key,
upload_id,
part_number,
SizedFileAdapter(fileobj, num_to_upload)
)
return PartInfo(part_number, result.etag)
def main():
"""
主函數,執行文件上傳流程
"""
# 獲取待上傳文件的大小
total_size = os.path.getsize(filename)
# 設定分片大小為100KB,可根據需要調整
part_size = determine_part_size(total_size, preferred_size=100 * 1024)
# 初始化分片上傳,返回upload_id
upload_id = bucket.init_multipart_upload(key).upload_id
part_info_list = []
# 創建線程池,設置最大并發數為8,可根據實際情況調整
max_workers = 8
with ThreadPoolExecutor(max_workers=max_workers) as executor:
futures = []
part_number = 1
offset = 0
# 循環上傳所有分片
while offset < total_size:
num_to_upload = min(part_size, total_size - offset)
# 提交上傳任務到線程池
futures.append(
executor.submit(upload_part, filename, bucket, key, upload_id, part_number, offset, num_to_upload)
)
offset += num_to_upload
part_number += 1
# 獲取每個任務的返回結果(PartInfo對象),并將其添加到part_info_list中
for future in futures:
part_info_list.append(future.result())
# 完成分片上傳并返回結果
bucket.complete_multipart_upload(key, upload_id, part_info_list)
print(f'Upload completed for {key}') # 輸出上傳完成的文件名
if __name__ == "__main__":
main() # 執行上傳操作
使用命令行工具ossutil
關于使用ossutil分片上傳具體操作, 請參見cp(上傳文件)。
使用REST API
如果您的程序自定義要求較高,您可以直接發起REST API請求。直接發起REST API請求需要手動編寫代碼計算簽名。更多信息,請參見InitiateMultipartUpload。
常見問題
如何刪除碎片?
分片上傳過程被中斷后,已上傳的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手動合并分片。