Skip to content

非公式本サイトは非公式の日本語ドキュメントであり、Cloudflare 公式サイトではありません。最新情報はdevelopers.cloudflare.comをご確認ください。

オブジェクトをアップロードする

最終更新 Markdown で表示Agent セットアップ

R2 へオブジェクトをアップロードする方法はいくつかあります。どの方法を選ぶかは、オブジェクトのサイズと性能要件によります。

アップロード方法を選ぶ

単一アップロード(PUT マルチパートアップロード
向いている用途 中小サイズのファイル(約 100 MB 未満) 大きいファイル、または並列化と再開が必要な場合
オブジェクトの最大サイズ 5 GiB 5 TiB(最大 10,000 パート)
パートサイズ 該当なし 1 パートあたり 5 MiB ~ 5 GiB
再開可能 いいえ — アップロード全体をやり直す必要があります はい — 失敗したパートだけ再試行します
並列アップロード いいえ はい — パートを同時にアップロードできます
使うタイミング 小さいオブジェクトの簡易なアップロード 動画、バックアップ、データセットなど、信頼性が重要なファイル

ダッシュボードからアップロードする

Cloudflare ダッシュボードからバケットへオブジェクトをアップロードする手順は次のとおりです。

  1. Cloudflare ダッシュボードで R2 object storage ページを開きます。

    Overview を開く ↗
  2. バケットを選びます。

  3. Upload を選びます。

  4. ファイルをアップロード領域へドラッグアンドドロップするか、select from computer を選びます。

アップロードが成功すると、確認メッセージが表示されます。

フォルダーを作成する

ダッシュボードで Create folder を選んでフォルダーを作成することもできます。末尾が / のキーを持つ 0 バイトのオブジェクトがプレースホルダーとして作られます。R2 でのフォルダーの仕組みは、プレフィックスとフォルダー を参照してください。

Workers API からアップロードする

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 リファレンス を参照してください。

署名付き URL(Workers)

クライアント(ブラウザ、モバイルアプリ)が 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 API からアップロードする

S3 互換 SDK でオブジェクトをアップロードします。アカウント IDR2 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

署名付き URL(S3 API)

ユーザーがサーバーを経由せずに 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 client
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 client
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",
)

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 ドキュメント を参照してください。

CLI からアップロードする

Rclone

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_name

rclone ls でアップロードを確認します。

rclone ls r2:bucket_name

詳細は rclone の例 を参照してください。

Wrangler

Wrangler でオブジェクトをアップロードします。r2 object put コマンド を実行します。

wrangler r2 object put test-bucket/image.png --file=image.png

省略可能なフラグで、Content-Type(MIME タイプ)、Content-DispositionCache-Control、そのほかの HTTP ヘッダーメタデータを設定できます。

マルチパートアップロードの詳細

パートサイズの上限

  • 最小パートサイズ: 5 MiB(最後のパートを除く)
  • 最大パートサイズ: 5 GiB
  • パート数の上限: 10,000
  • 最後以外のパートは、すべて同じサイズである必要があります

未完了アップロードのライフサイクル

未完了のマルチパートアップロードは、デフォルトで 7 日後に自動で中止されます。カスタムライフサイクルポリシーを設定する ことで変更できます。

ETag

マルチパートでアップロードしたオブジェクトの ETag は、単一の PUT でアップロードしたオブジェクトとは異なります。各パートの ETag は、そのパート内容の MD5 ハッシュです。完了したマルチパートオブジェクトの ETag は、すべてのパートのバイナリ MD5 合計を連結したハッシュのあとに、ハイフンとパート数を付けた値です。

たとえば、2 パートのアップロードでパートの ETag が bce6bf66aeb76c7040fdd5f4eccb78e68165449fc15bbf43d3b674595cbcc406 の場合、完了したオブジェクトの ETag は f77dc0eecdebcd774a2a22cb393ad2ff-2 になります。

関連リソース

署名付き URL

クライアント側アクセス用の、一時的なアップロードおよびダウンロード URL を生成します。

役に立ちましたか?