R2 へオブジェクトをアップロードする方法はいくつかあります。どの方法を選ぶかは、オブジェクトのサイズと性能要件によります。
単一アップロード(PUT) |
マルチパートアップロード | |
|---|---|---|
| 向いている用途 | 中小サイズのファイル(約 100 MB 未満) | 大きいファイル、または並列化と再開が必要な場合 |
| オブジェクトの最大サイズ | 5 GiB | 5 TiB(最大 10,000 パート) |
| パートサイズ | 該当なし | 1 パートあたり 5 MiB ~ 5 GiB |
| 再開可能 | いいえ — アップロード全体をやり直す必要があります | はい — 失敗したパートだけ再試行します |
| 並列アップロード | いいえ | はい — パートを同時にアップロードできます |
| 使うタイミング | 小さいオブジェクトの簡易なアップロード | 動画、バックアップ、データセットなど、信頼性が重要なファイル |
Cloudflare ダッシュボードからバケットへオブジェクトをアップロードする手順は次のとおりです。
-
Cloudflare ダッシュボードで R2 object storage ページを開きます。
Overview を開く ↗ -
バケットを選びます。
-
Upload を選びます。
-
ファイルをアップロード領域へドラッグアンドドロップするか、select from computer を選びます。
アップロードが成功すると、確認メッセージが表示されます。
ダッシュボードで Create folder を選んでフォルダーを作成することもできます。末尾が / のキーを持つ 0 バイトのオブジェクトがプレースホルダーとして作られます。R2 でのフォルダーの仕組みは、プレフィックスとフォルダー を参照してください。
Workers の R2 バインディング を使い、サーバー側でオブジェクトをアップロードします。R2 バインディングの設定手順は Workers から R2 を使う を参照してください。
put() を使い、1 回のリクエストでオブジェクトをアップロードします。中小サイズのオブジェクトでは、いちばん簡単な方法です。
export default {
async fetch(request, env) {
try {
const object = await env.MY_BUCKET.put("image.png", request.body, {
httpMetadata: {
contentType: "image/png",
},
});
if (object === null) {
return new Response("Precondition failed or upload returned null", {
status: 412,
});
}
return Response.json({
key: object.key,
size: object.size,
etag: object.etag,
});
} catch (err) {
return new Response(`Upload failed: ${err}`, { status: 500 });
}
},
};export default {
async fetch(request: Request, env: Env): Promise<Response> {
try {
const object = await env.MY_BUCKET.put("image.png", request.body, {
httpMetadata: {
contentType: "image/png",
},
});
if (object === null) {
return new Response("Precondition failed or upload returned null", {
status: 412,
});
}
return Response.json({
key: object.key,
size: object.size,
etag: object.etag,
});
} catch (err) {
return new Response(`Upload failed: ${err}`, { status: 500 });
}
},
} satisfies ExportedHandler<Env>;大きいファイルや、パートを並列にアップロードしたい場合は、createMultipartUpload() と resumeMultipartUpload() を使います。各パートは(最後のパートを除き)最低 5 MiB 必要です。
export default {
async fetch(request, env) {
const key = "large-file.bin";
// Create a new multipart upload
const multipartUpload = await env.MY_BUCKET.createMultipartUpload(key);
try {
// In a real application, these would be actual data chunks.
// Each part except the last must be at least 5 MiB.
const firstChunk = new Uint8Array(5 * 1024 * 1024); // placeholder
const secondChunk = new Uint8Array(1024); // placeholder
const part1 = await multipartUpload.uploadPart(1, firstChunk);
const part2 = await multipartUpload.uploadPart(2, secondChunk);
// Complete the upload with all parts
const object = await multipartUpload.complete([part1, part2]);
return Response.json({
key: object.key,
etag: object.httpEtag,
});
} catch (err) {
// Abort on failure so incomplete uploads do not count against storage
await multipartUpload.abort();
return new Response(`Multipart upload failed: ${err}`, { status: 500 });
}
},
};export default {
async fetch(request: Request, env: Env): Promise<Response> {
const key = "large-file.bin";
// Create a new multipart upload
const multipartUpload = await env.MY_BUCKET.createMultipartUpload(key);
try {
// In a real application, these would be actual data chunks.
// Each part except the last must be at least 5 MiB.
const firstChunk = new Uint8Array(5 * 1024 * 1024); // placeholder
const secondChunk = new Uint8Array(1024); // placeholder
const part1 = await multipartUpload.uploadPart(1, firstChunk);
const part2 = await multipartUpload.uploadPart(2, secondChunk);
// Complete the upload with all parts
const object = await multipartUpload.complete([part1, part2]);
return Response.json({
key: object.key,
etag: object.httpEtag,
});
} catch (err) {
// Abort on failure so incomplete uploads do not count against storage
await multipartUpload.abort();
return new Response(`Multipart upload failed: ${err}`, { status: 500 });
}
},
} satisfies ExportedHandler<Env>;多くの場合、マルチパートの状態(uploadId とアップロード済みパートの ETag)は、Worker へリクエストを送るクライアント側で追跡します。次の例は、クライアントアプリケーションが呼び出してマルチパートアップロードの作成、パートのアップロード、完了を行う HTTP API です。
export default {
async fetch(request, env) {
const url = new URL(request.url);
const key = url.pathname.slice(1);
const action = url.searchParams.get("action");
if (!key || !action) {
return new Response("Missing key or action", { status: 400 });
}
switch (action) {
// Step 1: Client calls POST /<key>?action=mpu-create
case "mpu-create": {
const upload = await env.MY_BUCKET.createMultipartUpload(key);
return Response.json({ key: upload.key, uploadId: upload.uploadId });
}
// Step 2: Client calls PUT /<key>?action=mpu-uploadpart&uploadId=...&partNumber=...
case "mpu-uploadpart": {
const uploadId = url.searchParams.get("uploadId");
const partNumber = Number(url.searchParams.get("partNumber"));
if (!uploadId || !partNumber || !request.body) {
return new Response("Missing uploadId, partNumber, or body", {
status: 400,
});
}
const upload = env.MY_BUCKET.resumeMultipartUpload(key, uploadId);
try {
const part = await upload.uploadPart(partNumber, request.body);
return Response.json(part);
} catch (err) {
return new Response(String(err), { status: 400 });
}
}
// Step 3: Client calls POST /<key>?action=mpu-complete&uploadId=...
case "mpu-complete": {
const uploadId = url.searchParams.get("uploadId");
if (!uploadId) {
return new Response("Missing uploadId", { status: 400 });
}
const upload = env.MY_BUCKET.resumeMultipartUpload(key, uploadId);
const body = await request.json();
try {
const object = await upload.complete(body.parts);
return new Response(null, {
headers: { etag: object.httpEtag },
});
} catch (err) {
return new Response(String(err), { status: 400 });
}
}
// Abort an in-progress upload
case "mpu-abort": {
const uploadId = url.searchParams.get("uploadId");
if (!uploadId) {
return new Response("Missing uploadId", { status: 400 });
}
const upload = env.MY_BUCKET.resumeMultipartUpload(key, uploadId);
try {
await upload.abort();
} catch (err) {
return new Response(String(err), { status: 400 });
}
return new Response(null, { status: 204 });
}
default:
return new Response(`Unknown action: ${action}`, { status: 400 });
}
},
};export default {
async fetch(request: Request, env: Env): Promise<Response> {
const url = new URL(request.url);
const key = url.pathname.slice(1);
const action = url.searchParams.get("action");
if (!key || !action) {
return new Response("Missing key or action", { status: 400 });
}
switch (action) {
// Step 1: Client calls POST /<key>?action=mpu-create
case "mpu-create": {
const upload = await env.MY_BUCKET.createMultipartUpload(key);
return Response.json({ key: upload.key, uploadId: upload.uploadId });
}
// Step 2: Client calls PUT /<key>?action=mpu-uploadpart&uploadId=...&partNumber=...
case "mpu-uploadpart": {
const uploadId = url.searchParams.get("uploadId");
const partNumber = Number(url.searchParams.get("partNumber"));
if (!uploadId || !partNumber || !request.body) {
return new Response("Missing uploadId, partNumber, or body", {
status: 400,
});
}
const upload = env.MY_BUCKET.resumeMultipartUpload(key, uploadId);
try {
const part = await upload.uploadPart(partNumber, request.body);
return Response.json(part);
} catch (err) {
return new Response(String(err), { status: 400 });
}
}
// Step 3: Client calls POST /<key>?action=mpu-complete&uploadId=...
case "mpu-complete": {
const uploadId = url.searchParams.get("uploadId");
if (!uploadId) {
return new Response("Missing uploadId", { status: 400 });
}
const upload = env.MY_BUCKET.resumeMultipartUpload(key, uploadId);
const body = await request.json<{ parts: R2UploadedPart[] }>();
try {
const object = await upload.complete(body.parts);
return new Response(null, {
headers: { etag: object.httpEtag },
});
} catch (err) {
return new Response(String(err), { status: 400 });
}
}
// Abort an in-progress upload
case "mpu-abort": {
const uploadId = url.searchParams.get("uploadId");
if (!uploadId) {
return new Response("Missing uploadId", { status: 400 });
}
const upload = env.MY_BUCKET.resumeMultipartUpload(key, uploadId);
try {
await upload.abort();
} catch (err) {
return new Response(String(err), { status: 400 });
}
return new Response(null, { status: 204 });
}
default:
return new Response(`Unknown action: ${action}`, { status: 400 });
}
},
} satisfies ExportedHandler<Env>;Workers API の完全なリファレンスは、Workers API リファレンス を参照してください。
クライアント(ブラウザ、モバイルアプリ)が Worker を経由せずに R2 へ直接アップロードする必要がある場合は、サーバー側で署名付き URL を生成し、クライアントへ渡します。
import { AwsClient } from "aws4fetch";
export default {
async fetch(request, env) {
const r2 = new AwsClient({
accessKeyId: env.R2_ACCESS_KEY_ID,
secretAccessKey: env.R2_SECRET_ACCESS_KEY,
});
// Generate a presigned PUT URL valid for 1 hour
const url = new URL(
"https://<ACCOUNT_ID>.r2.cloudflarestorage.com/my-bucket/image.png",
);
url.searchParams.set("X-Amz-Expires", "3600");
const signed = await r2.sign(new Request(url, { method: "PUT" }), {
aws: { signQuery: true },
});
// Return the signed URL to the client — they can PUT directly to R2
return Response.json({ url: signed.url });
},
};import { AwsClient } from "aws4fetch";
interface Env {
R2_ACCESS_KEY_ID: string;
R2_SECRET_ACCESS_KEY: string;
}
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const r2 = new AwsClient({
accessKeyId: env.R2_ACCESS_KEY_ID,
secretAccessKey: env.R2_SECRET_ACCESS_KEY,
});
// Generate a presigned PUT URL valid for 1 hour
const url = new URL(
"https://<ACCOUNT_ID>.r2.cloudflarestorage.com/my-bucket/image.png",
);
url.searchParams.set("X-Amz-Expires", "3600");
const signed = await r2.sign(new Request(url, { method: "PUT" }), {
aws: { signQuery: true },
});
// Return the signed URL to the client — they can PUT directly to R2
return Response.json({ url: signed.url });
},
} satisfies ExportedHandler<Env>;GET、PUT、セキュリティのベストプラクティスを含む署名付き URL の詳細は、署名付き URL を参照してください。
S3 互換 SDK でオブジェクトをアップロードします。アカウント ID と R2 API トークン が必要です。
import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";
import { readFile } from "node:fs/promises";
const S3 = new S3Client({
region: "auto",
endpoint: `https://<ACCOUNT_ID>.r2.cloudflarestorage.com`,
credentials: {
accessKeyId: "<ACCESS_KEY_ID>",
secretAccessKey: "<SECRET_ACCESS_KEY>",
},
});
const fileContent = await readFile("./image.png");
const response = await S3.send(
new PutObjectCommand({
Bucket: "my-bucket",
Key: "image.png",
Body: fileContent,
ContentType: "image/png",
}),
);
console.log(`Uploaded successfully. ETag: ${response.ETag}`);import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";
import { readFile } from "node:fs/promises";
const S3 = new S3Client({
region: "auto",
endpoint: `https://<ACCOUNT_ID>.r2.cloudflarestorage.com`,
credentials: {
accessKeyId: "<ACCESS_KEY_ID>",
secretAccessKey: "<SECRET_ACCESS_KEY>",
},
});
const fileContent = await readFile("./image.png");
const response = await S3.send(
new PutObjectCommand({
Bucket: "my-bucket",
Key: "image.png",
Body: fileContent,
ContentType: "image/png",
}),
);
console.log(`Uploaded successfully. ETag: ${response.ETag}`);import boto3
s3 = boto3.client(
service_name="s3",
endpoint_url="https://<ACCOUNT_ID>.r2.cloudflarestorage.com",
aws_access_key_id="<ACCESS_KEY_ID>",
aws_secret_access_key="<SECRET_ACCESS_KEY>",
region_name="auto",
)
with open("./image.png", "rb") as f:
response = s3.put_object(
Bucket="my-bucket",
Key="image.png",
Body=f,
ContentType="image/png",
)
print(f"Uploaded successfully. ETag: {response['ETag']}")ほとんどの S3 SDK は、ファイルが設定可能なしきい値を超えると、マルチパートアップロードを自動で処理します。次の例では、自動(高レベル)と手動(低レベル)の両方を示します。
SDK がファイルを分割し、パートを並列にアップロードします。
import { S3Client } from "@aws-sdk/client-s3";
import { Upload } from "@aws-sdk/lib-storage";
import { createReadStream } from "node:fs";
const S3 = new S3Client({
region: "auto",
endpoint: `https://<ACCOUNT_ID>.r2.cloudflarestorage.com`,
credentials: {
accessKeyId: "<ACCESS_KEY_ID>",
secretAccessKey: "<SECRET_ACCESS_KEY>",
},
});
const upload = new Upload({
client: S3,
params: {
Bucket: "my-bucket",
Key: "large-file.bin",
Body: createReadStream("./large-file.bin"),
},
// Upload parts in parallel (default: 4)
leavePartsOnError: false,
});
upload.on("httpUploadProgress", (progress) => {
console.log(`Uploaded ${progress.loaded ?? 0} bytes`);
});
const result = await upload.done();
console.log(`Upload complete. ETag: ${result.ETag}`);import { S3Client } from "@aws-sdk/client-s3";
import { Upload } from "@aws-sdk/lib-storage";
import { createReadStream } from "node:fs";
const S3 = new S3Client({
region: "auto",
endpoint: `https://<ACCOUNT_ID>.r2.cloudflarestorage.com`,
credentials: {
accessKeyId: "<ACCESS_KEY_ID>",
secretAccessKey: "<SECRET_ACCESS_KEY>",
},
});
const upload = new Upload({
client: S3,
params: {
Bucket: "my-bucket",
Key: "large-file.bin",
Body: createReadStream("./large-file.bin"),
},
leavePartsOnError: false,
});
upload.on("httpUploadProgress", (progress) => {
console.log(`Uploaded ${progress.loaded ?? 0} bytes`);
});
const result = await upload.done();
console.log(`Upload complete. ETag: ${result.ETag}`);import boto3
s3 = boto3.client(
service_name="s3",
endpoint_url="https://<ACCOUNT_ID>.r2.cloudflarestorage.com",
aws_access_key_id="<ACCESS_KEY_ID>",
aws_secret_access_key="<SECRET_ACCESS_KEY>",
region_name="auto",
)
# upload_file automatically uses multipart for large files.
# For better throughput with large objects, use the manual multipart example below.
s3.upload_file(
Filename="./large-file.bin",
Bucket="my-bucket",
Key="large-file.bin",
)パートサイズやアップロード順を細かく制御したい場合は、低レベル API を使います。
import {
S3Client,
CreateMultipartUploadCommand,
UploadPartCommand,
CompleteMultipartUploadCommand,
AbortMultipartUploadCommand,
type CompletedPart,
} from "@aws-sdk/client-s3";
import { createReadStream, statSync } from "node:fs";
const S3 = new S3Client({
region: "auto",
endpoint: `https://<ACCOUNT_ID>.r2.cloudflarestorage.com`,
credentials: {
accessKeyId: "<ACCESS_KEY_ID>",
secretAccessKey: "<SECRET_ACCESS_KEY>",
},
});
const bucket = "my-bucket";
const key = "large-file.bin";
const partSize = 10 * 1024 * 1024; // 10 MiB per part
// Step 1: Create the multipart upload
const { UploadId } = await S3.send(
new CreateMultipartUploadCommand({ Bucket: bucket, Key: key }),
);
try {
const fileSize = statSync("./large-file.bin").size;
const partCount = Math.ceil(fileSize / partSize);
const parts: CompletedPart[] = [];
// Step 2: Upload each part
for (let i = 0; i < partCount; i++) {
const start = i * partSize;
const end = Math.min(start + partSize, fileSize);
const { ETag } = await S3.send(
new UploadPartCommand({
Bucket: bucket,
Key: key,
UploadId,
PartNumber: i + 1,
Body: createReadStream("./large-file.bin", { start, end: end - 1 }),
ContentLength: end - start,
}),
);
parts.push({ PartNumber: i + 1, ETag });
}
// Step 3: Complete the upload
await S3.send(
new CompleteMultipartUploadCommand({
Bucket: bucket,
Key: key,
UploadId,
MultipartUpload: { Parts: parts },
}),
);
console.log("Multipart upload complete.");
} catch (err) {
// Abort on failure to clean up incomplete parts
try {
await S3.send(
new AbortMultipartUploadCommand({ Bucket: bucket, Key: key, UploadId }),
);
} catch (_abortErr) {
// Best-effort cleanup — the original error is more important
}
throw err;
}import {
S3Client,
CreateMultipartUploadCommand,
UploadPartCommand,
CompleteMultipartUploadCommand,
AbortMultipartUploadCommand,
} from "@aws-sdk/client-s3";
import { createReadStream, statSync } from "node:fs";
const S3 = new S3Client({
region: "auto",
endpoint: `https://<ACCOUNT_ID>.r2.cloudflarestorage.com`,
credentials: {
accessKeyId: "<ACCESS_KEY_ID>",
secretAccessKey: "<SECRET_ACCESS_KEY>",
},
});
const bucket = "my-bucket";
const key = "large-file.bin";
const partSize = 10 * 1024 * 1024; // 10 MiB per part
// Step 1: Create the multipart upload
const { UploadId } = await S3.send(
new CreateMultipartUploadCommand({ Bucket: bucket, Key: key }),
);
try {
const fileSize = statSync("./large-file.bin").size;
const partCount = Math.ceil(fileSize / partSize);
const parts = [];
// Step 2: Upload each part
for (let i = 0; i < partCount; i++) {
const start = i * partSize;
const end = Math.min(start + partSize, fileSize);
const { ETag } = await S3.send(
new UploadPartCommand({
Bucket: bucket,
Key: key,
UploadId,
PartNumber: i + 1,
Body: createReadStream("./large-file.bin", { start, end: end - 1 }),
ContentLength: end - start,
}),
);
parts.push({ PartNumber: i + 1, ETag });
}
// Step 3: Complete the upload
await S3.send(
new CompleteMultipartUploadCommand({
Bucket: bucket,
Key: key,
UploadId,
MultipartUpload: { Parts: parts },
}),
);
console.log("Multipart upload complete.");
} catch (err) {
// Abort on failure to clean up incomplete parts
try {
await S3.send(
new AbortMultipartUploadCommand({ Bucket: bucket, Key: key, UploadId }),
);
} catch (_abortErr) {
// Best-effort cleanup — the original error is more important
}
throw err;
}import boto3
import math
import os
from concurrent.futures import ThreadPoolExecutor
s3 = boto3.client(
service_name="s3",
endpoint_url="https://<ACCOUNT_ID>.r2.cloudflarestorage.com",
aws_access_key_id="<ACCESS_KEY_ID>",
aws_secret_access_key="<SECRET_ACCESS_KEY>",
region_name="auto",
)
bucket = "my-bucket"
key = "large-file.bin"
file_path = "./large-file.bin"
part_size = 16 * 1024 * 1024 # 16 MiB per part
max_workers = 10 # Number of parallel upload threads
# Step 1: Create the multipart upload
upload_id = None
mpu = s3.create_multipart_upload(Bucket=bucket, Key=key)
upload_id = mpu["UploadId"]
def upload_part(part_number, data):
response = s3.upload_part(
Bucket=bucket,
Key=key,
UploadId=upload_id,
PartNumber=part_number,
Body=data,
)
return {"PartNumber": part_number, "ETag": response["ETag"]}
try:
file_size = os.path.getsize(file_path)
part_count = math.ceil(file_size / part_size)
# Step 2: Upload parts in parallel
with ThreadPoolExecutor(max_workers=max_workers) as pool:
futures = []
with open(file_path, "rb") as f:
for i in range(part_count):
data = f.read(part_size)
futures.append(pool.submit(upload_part, i + 1, data))
parts = [future.result() for future in futures]
# Step 3: Complete the upload
s3.complete_multipart_upload(
Bucket=bucket,
Key=key,
UploadId=upload_id,
MultipartUpload={"Parts": parts},
)
print("Multipart upload complete.")
except Exception:
# Abort on failure to clean up incomplete parts
if upload_id:
try:
s3.abort_multipart_upload(Bucket=bucket, Key=key, UploadId=upload_id)
except Exception:
pass # Best-effort cleanup — the original error is more important
raiseユーザーがサーバーを経由せずに R2 へ直接アップロードするクライアント側アップロードでは、署名付き PUT URL を生成します。サーバーが URL を作り、クライアントがその URL へアップロードします。API 認証情報はクライアントに公開されません。
import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
const S3 = new S3Client({
region: "auto",
endpoint: `https://<ACCOUNT_ID>.r2.cloudflarestorage.com`,
credentials: {
accessKeyId: "<ACCESS_KEY_ID>",
secretAccessKey: "<SECRET_ACCESS_KEY>",
},
});
const presignedUrl = await getSignedUrl(
S3,
new PutObjectCommand({
Bucket: "my-bucket",
Key: "user-upload.png",
ContentType: "image/png",
}),
{ expiresIn: 3600 }, // Valid for 1 hour
);
console.log(presignedUrl);
// Return presignedUrl to the clientimport { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
const S3 = new S3Client({
region: "auto",
endpoint: `https://<ACCOUNT_ID>.r2.cloudflarestorage.com`,
credentials: {
accessKeyId: "<ACCESS_KEY_ID>",
secretAccessKey: "<SECRET_ACCESS_KEY>",
},
});
const presignedUrl = await getSignedUrl(
S3,
new PutObjectCommand({
Bucket: "my-bucket",
Key: "user-upload.png",
ContentType: "image/png",
}),
{ expiresIn: 3600 }, // Valid for 1 hour
);
console.log(presignedUrl);
// Return presignedUrl to the clientimport boto3
s3 = boto3.client(
service_name="s3",
endpoint_url="https://<ACCOUNT_ID>.r2.cloudflarestorage.com",
aws_access_key_id="<ACCESS_KEY_ID>",
aws_secret_access_key="<SECRET_ACCESS_KEY>",
region_name="auto",
)
presigned_url = s3.generate_presigned_url(
"put_object",
Params={
"Bucket": "my-bucket",
"Key": "user-upload.png",
"ContentType": "image/png",
},
ExpiresIn=3600, # Valid for 1 hour
)
print(presigned_url)
# Return presigned_url to the client署名付き URL の詳細は、署名付き URL を参照してください。
対応しているすべての S3 API メソッドは、R2 の S3 API ドキュメント を参照してください。
Rclone ↗ は、クラウドストレージ上のファイルを管理するコマンドラインツールです。ローカルマシンからの複数ファイルのアップロードや、ほかのクラウドストレージからのデータコピーに適しています。
rclone を使うには、公式ドキュメント Install rclone ↗ に従ってマシンへインストールします。
rclone copy コマンドでファイルをアップロードします。
# Upload a single file
rclone copy /path/to/local/image.png r2:bucket_name
# Upload everything in a directory
rclone copy /path/to/local/folder r2:bucket_namerclone ls でアップロードを確認します。
rclone ls r2:bucket_name詳細は rclone の例 を参照してください。
Wrangler でオブジェクトをアップロードします。r2 object put コマンド を実行します。
wrangler r2 object put test-bucket/image.png --file=image.png省略可能なフラグで、Content-Type(MIME タイプ)、Content-Disposition、Cache-Control、そのほかの HTTP ヘッダーメタデータを設定できます。
- 最小パートサイズ: 5 MiB(最後のパートを除く)
- 最大パートサイズ: 5 GiB
- パート数の上限: 10,000
- 最後以外のパートは、すべて同じサイズである必要があります
未完了のマルチパートアップロードは、デフォルトで 7 日後に自動で中止されます。カスタムライフサイクルポリシーを設定する ことで変更できます。
マルチパートでアップロードしたオブジェクトの ETag は、単一の PUT でアップロードしたオブジェクトとは異なります。各パートの ETag は、そのパート内容の MD5 ハッシュです。完了したマルチパートオブジェクトの ETag は、すべてのパートのバイナリ MD5 合計を連結したハッシュのあとに、ハイフンとパート数を付けた値です。
たとえば、2 パートのアップロードでパートの ETag が bce6bf66aeb76c7040fdd5f4eccb78e6 と 8165449fc15bbf43d3b674595cbcc406 の場合、完了したオブジェクトの ETag は f77dc0eecdebcd774a2a22cb393ad2ff-2 になります。