Workers Kết Nối R2: Hướng Dẫn Tích Hợp & Upload File Lớn

Workers kết nối R2 là cơ chế cho phép một Cloudflare Worker truy cập trực tiếp vào bucket R2 thông qua R2 binding, thay vì phải gọi qua REST API hay S3-compatible API công khai. Cách tích hợp này giúp giảm độ trễ, loại bỏ egress fee khi đọc/ghi dữ liệu, và đơn giản hóa việc xây dựng ứng dụng edge có lưu trữ object.


I. Workers kết nối R2 là gì?

R2 binding là một biến môi trường đặc biệt được khai báo trong cấu hình của Worker, cho phép code chạy trên Worker gọi trực tiếp các thao tác đọc/ghi/xóa/liệt kê object trong một bucket R2 cụ thể — mà không cần đi qua endpoint HTTP công khai hay xác thực bằng access key/secret key kiểu S3.

Nói cách khác, khi bạn "bind" một bucket R2 vào Worker, bucket đó trở thành một object có sẵn trong runtime của Worker (thường được gọi tên là env.MY_BUCKET), và bạn thao tác với nó bằng API JavaScript native thay vì gửi HTTP request.

1. Vì sao dùng binding thay vì gọi R2 qua REST API/S3 API thông thường

  • Không cần quản lý credential: Binding được cấp quyền ở tầng cấu hình (wrangler.toml), Worker không cần lưu access key/secret key trong code hay biến môi trường.
  • Độ trễ thấp hơn: Request không phải đi vòng qua public endpoint rồi xác thực lại — logic Worker và dữ liệu R2 nằm trong cùng runtime edge.
  • Giảm bề mặt tấn công: Vì không expose endpoint S3-compatible ra ngoài, rủi ro lộ credential hoặc bị brute-force giảm đáng kể.

II. Vì sao nên tích hợp Workers với R2? (lợi ích cho enterprise)

Đối với doanh nghiệp đang cân nhắc xây dựng ứng dụng edge có lưu trữ object (upload file, cache tài sản tĩnh, xử lý log), tích hợp Workers–R2 mang lại ba lợi ích chính:

1. Zero egress fee khi đọc/ghi dữ liệu từ Worker

R2 được thiết kế không tính phí egress (băng thông ra ngoài) — đây là điểm khác biệt lớn so với các dịch vụ object storage truyền thống. Khi Worker đọc dữ liệu từ R2 để trả về cho người dùng cuối, doanh nghiệp không phát sinh thêm chi phí băng thông cho lượt đọc đó.

2. Độ trễ thấp — xử lý logic và lưu trữ cùng nằm trên edge network

Vì cả Worker (compute) và R2 (storage) đều chạy trên hạ tầng edge toàn cầu của Cloudflare, việc truy vấn dữ liệu không cần round-trip về một region trung tâm như kiến trúc origin-server truyền thống.

3. Use case enterprise phổ biến

  • Lưu trữ và truy vấn log ứng dụng theo thời gian thực
  • Cache tài sản tĩnh (hình ảnh, file media) kèm logic xử lý tùy biến (resize, watermark) ngay tại Worker
  • Xử lý upload từ người dùng (form nộp hồ sơ, tài liệu) mà không cần server backend riêng
  • Xây dựng pipeline xử lý dữ liệu dạng event-driven giữa các dịch vụ Cloudflare (Workers, Queues, R2)

III. Điều kiện chuẩn bị trước khi tích hợp

Trước khi cấu hình binding, hệ thống cần đáp ứng các điều kiện sau:

1. Tài khoản Cloudflare có bật Workers và R2

Cả hai dịch vụ cần được kích hoạt trên cùng một tài khoản/zone Cloudflare. Với gói Enterprise, việc bật các dịch vụ này thường được cấu hình sẵn trong quá trình onboarding.

2. Cài đặt và đăng nhập Wrangler CLI

Wrangler là công cụ dòng lệnh chính thức để quản lý cấu hình, deploy và test Worker. Cần cài đặt và đăng nhập trước khi thực hiện bất kỳ thao tác nào với R2 hay Worker qua dòng lệnh:

npm install -g wrangler

wrangler login

Lệnh wrangler login sẽ mở trình duyệt để xác thực với tài khoản Cloudflare — cần hoàn tất bước này trước khi chạy các lệnh tạo bucket hay deploy ở dưới.

3. Tạo bucket R2

Sau khi đã đăng nhập, tạo bucket qua Cloudflare Dashboard hoặc qua Wrangler CLI:

npx wrangler r2 bucket create ten-bucket-cua-ban


IV. Hướng dẫn kết nối Workers với R2 từng bước

1. Khai báo R2 binding trong wrangler.toml

a. Cú pháp binding name, bucket_name

Trong file cấu hình wrangler.toml của project Worker, thêm mục [[r2_buckets]]:

toml
1name = "worker-r2-demo"
2main = "src/index.js"
3compatibility_date = "2026-06-01"
4
5[[r2_buckets]]
6binding = "MY_BUCKET"
7bucket_name = "ten-bucket-cua-ban"

Trong đó:

  • binding: tên biến sẽ dùng để truy cập bucket trong code Worker (ví dụ env.MY_BUCKET)
  • bucket_name: tên bucket R2 thực tế đã tạo ở bước III.3

b. Lưu ý về môi trường (dev/staging/production)

Nếu hệ thống có nhiều môi trường, nên khai báo bucket riêng cho từng môi trường để tránh dữ liệu dev/staging lẫn vào production:

toml
1[env.staging]
2[[env.staging.r2_buckets]]
3binding = "MY_BUCKET"
4bucket_name = "ten-bucket-staging"
5
6[env.production]
7[[env.production.r2_buckets]]
8binding = "MY_BUCKET"
9bucket_name = "ten-bucket-production"

2. Viết code Worker truy cập R2 binding

Sau khi khai báo binding, bucket sẽ xuất hiện trong tham số env của handler Worker.

a. Đọc object (GET)

JavaScript
1export default {
2  async fetch(request, env) {
3    const url = new URL(request.url);
4    const key = url.pathname.slice(1); // lấy tên file từ path
5
6    const object = await env.MY_BUCKET.get(key);
7
8    if (object === null) {
9      return new Response("Không tìm thấy file", { status: 404 });
10    }
11
12    const headers = new Headers();
13    object.writeHttpMetadata(headers);
14    headers.set("etag", object.httpEtag);
15
16    return new Response(object.body, { headers });
17  },
18};

b. Ghi object (PUT)

JavaScript
1export default {
2  async fetch(request, env) {
3    if (request.method !== "PUT") {
4      return new Response("Chỉ hỗ trợ PUT", { status: 405 });
5    }
6
7    const url = new URL(request.url);
8    const key = url.pathname.slice(1);
9
10    try {
11      // put() phù hợp cho object nhỏ/vừa (tối đa 5 GiB mỗi lần ghi)
12      const object = await env.MY_BUCKET.put(key, request.body, {
13        httpMetadata: {
14          contentType: request.headers.get("content-type") || "application/octet-stream",
15        },
16      });
17
18      return Response.json({
19        key: object.key,
20        size: object.size,
21        etag: object.etag,
22      });
23    } catch (err) {
24      return new Response(`Ghi file thất bại: ${err}`, { status: 500 });
25    }
26  },
27};

c. Xóa object (DELETE)

JavaScript
1export default {
2  async fetch(request, env) {
3    if (request.method !== "DELETE") {
4      return new Response("Chỉ hỗ trợ DELETE", { status: 405 });
5    }
6
7    const url = new URL(request.url);
8    const key = url.pathname.slice(1);
9
10    await env.MY_BUCKET.delete(key);
11
12    return new Response(`Đã xóa file: ${key}`, { status: 200 });
13  },
14};

d. List objects trong bucket

JavaScript
1export default {
2  async fetch(request, env) {
3    const listed = await env.MY_BUCKET.list();
4
5    const fileNames = listed.objects.map((obj) => obj.key);
6
7    return new Response(JSON.stringify(fileNames), {
8      headers: { "Content-Type": "application/json" },
9    });
10  },
11};

3. Deploy và kiểm thử

Sau khi hoàn tất code, deploy Worker bằng lệnh:

npx wrangler deploy

Để test cục bộ trước khi deploy, dùng chế độ dev của Wrangler (hỗ trợ giả lập binding R2 ngay trên máy local):

npx wrangler dev


V. Xử lý upload file rất lớn qua Workers–R2 như thế nào?

Khi upload file dung lượng lớn (video, backup, dataset...) trực tiếp qua Worker theo cách đơn giản ở mục IV — nhận toàn bộ request.body rồi put() một lần — hệ thống dễ gặp nghẽn vì Worker có giới hạn về kích thước request, thời gian CPU xử lý mỗi request, và khả năng chịu tải khi nhiều client upload đồng thời. Phần này trình bày các giải pháp để xử lý upload file lớn ổn định hơn.

1. Vì sao upload file lớn qua Worker theo cách đơn giản dễ bị nghẽn

  • Giới hạn kích thước object khi ghi một lần: Thao tác put() đơn (single upload) chỉ phù hợp với object tối đa khoảng 5 GiB, và không có cơ chế resume — nếu lỗi giữa chừng phải upload lại toàn bộ.
  • Nghẽn khi nhiều client upload đồng thời: Nếu toàn bộ luồng dữ liệu của mọi client đều đi qua cùng một Worker instance theo kiểu tuần tự (nhận hết file rồi mới ghi), tài nguyên CPU-time và concurrency của Worker dễ bị bão hòa vào giờ cao điểm.
  • Rủi ro khi upload thất bại giữa chừng: Với cách upload "một lần toàn bộ file", nếu mất kết nối ở phút thứ 9 của một file 2GB, client phải upload lại từ đầu, gây lãng phí băng thông và trải nghiệm kém.

2. Giải pháp: Multipart Upload — chia file thành nhiều phần, tải song song

R2 hỗ trợ multipart upload (tương thích chuẩn multipart của S3): file được chia thành nhiều phần (part), mỗi phần được tải lên độc lập, sau đó R2 ghép lại thành một object hoàn chỉnh. R2 binding trong Workers expose các method tương ứng (createMultipartUpload, resumeMultipartUpload, uploadPart, complete, abort) để thao tác multipart ngay trong code Worker.

Một số giới hạn kỹ thuật cần nắm khi thiết kế client chia part:

Thông số

Giá trị

Kích thước tối thiểu mỗi part (trừ part cuối)

5 MiB

Kích thước tối đa mỗi part

5 GiB

Số lượng part tối đa

10.000 part

Dung lượng object tối đa qua multipart

~5 TiB

Thời gian tự hủy phiên upload dở dang

7 ngày (mặc định, có thể cấu hình qua lifecycle policy)

a. Khởi tạo phiên multipart upload

JavaScript
1export default {
2  async fetch(request, env) {
3    const url = new URL(request.url);
4    const key = url.pathname.slice(1);
5
6    // Bắt đầu một phiên multipart upload mới, R2 trả về uploadId
7    // để client dùng cho các bước upload part và hoàn tất phía sau
8    const multipartUpload = await env.MY_BUCKET.createMultipartUpload(key);
9
10    return Response.json({
11      key: multipartUpload.key,
12      uploadId: multipartUpload.uploadId,
13    });
14  },
15};

b. Upload từng phần (part)

Client chia file thành các chunk (tối thiểu 5 MiB/phần, trừ phần cuối) và gửi từng phần kèm uploadId và số thứ tự partNumber. Worker dùng resumeMultipartUpload() để "nối lại" đúng phiên upload đang dở dựa trên uploadId đã cấp ở bước trên:

JavaScript
1export default {
2  async fetch(request, env) {
3    const url = new URL(request.url);
4    const key = url.pathname.slice(1);
5    const uploadId = url.searchParams.get("uploadId");
6    const partNumber = Number(url.searchParams.get("partNumber"));
7
8    if (!uploadId || !partNumber || !request.body) {
9      return new Response("Thiếu uploadId, partNumber hoặc body", { status: 400 });
10    }
11
12    const multipartUpload = env.MY_BUCKET.resumeMultipartUpload(key, uploadId);
13
14    try {
15      // uploadPart() trả về etag của part — cần lưu lại để dùng ở bước complete
16      const uploadedPart = await multipartUpload.uploadPart(partNumber, request.body);
17      return Response.json({ partNumber, etag: uploadedPart.etag });
18    } catch (err) {
19      // Chỉ phần này bị lỗi, các part khác đã upload thành công không bị ảnh hưởng
20      return new Response(`Upload part thất bại: ${err}`, { status: 400 });
21    }
22  },
23};

c. Hoàn tất hoặc hủy phiên upload

Sau khi tất cả các phần đã upload xong, client gửi danh sách partNumber + etag để Worker gọi ghép lại. Nếu có lỗi không thể khắc phục, nên chủ động gọi abort() để hủy phiên dở dang thay vì để hệ thống tự dọn sau 7 ngày:

JavaScript
1export default {
2  async fetch(request, env) {
3    const url = new URL(request.url);
4    const key = url.pathname.slice(1);
5    const uploadId = url.searchParams.get("uploadId");
6    const action = url.searchParams.get("action"); // "complete" hoặc "abort"
7
8    const multipartUpload = env.MY_BUCKET.resumeMultipartUpload(key, uploadId);
9
10    try {
11      if (action === "abort") {
12        await multipartUpload.abort();
13        return new Response("Đã hủy phiên upload", { status: 204 });
14      }
15
16      const { parts } = await request.json(); // [{ partNumber, etag }, ...]
17      const object = await multipartUpload.complete(parts);
18
19      return Response.json({ key: object.key, etag: object.httpEtag });
20    } catch (err) {
21      return new Response(`Xử lý upload thất bại: ${err}`, { status: 400 });
22    }
23  },
24};
25

3. Có tách ra nhiều luồng (multiple streams) để tăng tốc không?

Có. Vì mỗi part trong multipart upload là một request độc lập (có partNumber riêng), phía client hoàn toàn có thể mở nhiều kết nối song song để upload cùng lúc nhiều part khác nhau — thay vì upload tuần tự từng phần một. Đây là cách phổ biến để giảm tổng thời gian upload file lớn:

  • Chia file thành N phần bằng nhau (đúng giới hạn kích thước part ở bảng trên).
  • Giới hạn số luồng song song hợp lý (ví dụ 3–6 luồng cùng lúc) để tránh làm nghẽn ngược lại do quá nhiều request đồng thời tới cùng một Worker.
  • Mỗi luồng chỉ cần retry riêng phần của mình nếu thất bại, không ảnh hưởng đến các phần khác — đây là lợi ích lớn nhất so với upload nguyên khối.

4. Giải pháp thay thế: Presigned URL — để client upload thẳng lên R2, bỏ qua Worker

Với các hệ thống có lượng upload file lớn và tần suất cao, một phương án khác là dùng presigned URL (URL ký sẵn có thời hạn) theo chuẩn S3-compatible API của R2: Worker chỉ đóng vai trò xác thực người dùng và ký sẵn URL, còn việc truyền dữ liệu file thực tế diễn ra trực tiếp giữa client và R2 — không đi qua Worker. Cách này giảm tải hoàn toàn cho Worker khi xử lý luồng dữ liệu lớn, nhưng cần dùng access key/secret key theo chuẩn S3 (thường thông qua thư viện ký request như aws4fetch) thay vì R2 binding thuần túy:

JavaScript
1import { AwsClient } from "aws4fetch";
2
3export default {
4  async fetch(request, env) {
5    // TODO: kiểm tra xác thực người dùng trước khi cấp URL upload
6
7    const r2 = new AwsClient({
8      accessKeyId: env.R2_ACCESS_KEY_ID,
9      secretAccessKey: env.R2_SECRET_ACCESS_KEY,
10    });
11
12    const uploadUrl = new URL(
13      `https://<ACCOUNT_ID>.r2.cloudflarestorage.com/ten-bucket-cua-ban/${crypto.randomUUID()}`
14    );
15    uploadUrl.searchParams.set("X-Amz-Expires", "3600"); // URL có hiệu lực 1 giờ
16
17    const signedRequest = await r2.sign(new Request(uploadUrl, { method: "PUT" }), {
18      aws: { signQuery: true },
19    });
20
21    // Trả URL đã ký về cho client — client PUT file thẳng lên R2, không qua Worker
22    return Response.json({ url: signedRequest.url });
23  },
24};
25

5. Upload qua S3 API — dùng khi không chạy trong runtime Worker

Ngoài R2 binding (chỉ dùng được bên trong code Worker), R2 còn hỗ trợ S3-compatible API — cho phép upload từ bất kỳ đâu bằng các SDK S3 quen thuộc (AWS SDK cho JavaScript/Python, hoặc công cụ như rclone). Cách này phù hợp khi việc upload được thực hiện từ một backend/server riêng, script xử lý dữ liệu theo batch, hoặc pipeline CI/CD — không nằm trong luồng xử lý của Worker. Để dùng được, cần chuẩn bị account ID và R2 API token (access key/secret key) của Cloudflare.

a. Single upload qua S3 API

JavaScript
1import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";
2import { readFile } from "node:fs/promises";
3
4const S3 = new S3Client({
5  region: "auto", // R2 không phân vùng theo region như AWS nên luôn để "auto"
6  endpoint: "https://<ACCOUNT_ID>.r2.cloudflarestorage.com",
7  credentials: {
8    accessKeyId: "<ACCESS_KEY_ID>",
9    secretAccessKey: "<SECRET_ACCESS_KEY>",
10  },
11});
12
13const fileContent = await readFile("./bao-cao.pdf");
14
15const response = await S3.send(
16  new PutObjectCommand({
17    Bucket: "ten-bucket-cua-ban",
18    Key: "bao-cao.pdf",
19    Body: fileContent,
20    ContentType: "application/pdf",
21  })
22);
23
24console.log(`Upload thành công. ETag: ${response.ETag}`);
25

b. Multipart upload tự động (SDK tự chia phần)

Với file lớn, hầu hết SDK S3 (bao gồm module @aws-sdk/lib-storage) tự động chia file thành nhiều part và tải song song mà không cần tự viết logic chia part:

JavaScript
1import { S3Client } from "@aws-sdk/client-s3";
2import { Upload } from "@aws-sdk/lib-storage";
3import { createReadStream } from "node:fs";
4
5const S3 = new S3Client({
6  region: "auto",
7  endpoint: "https://<ACCOUNT_ID>.r2.cloudflarestorage.com",
8  credentials: {
9    accessKeyId: "<ACCESS_KEY_ID>",
10    secretAccessKey: "<SECRET_ACCESS_KEY>",
11  },
12});
13
14const upload = new Upload({
15  client: S3,
16  params: {
17    Bucket: "ten-bucket-cua-ban",
18    Key: "backup-database.bin",
19    Body: createReadStream("./backup-database.bin"),
20  },
21});
22
23// Theo dõi tiến độ upload — hữu ích khi cần hiển thị progress bar
24upload.on("httpUploadProgress", (progress) => {
25  console.log(`Đã upload: ${progress.loaded ?? 0} bytes`);
26});
27
28const result = await upload.done();
29console.log(`Upload hoàn tất. ETag: ${result.ETag}`);
30

c. Multipart upload thủ công — kiểm soát số luồng song song

Khi cần chủ động kiểm soát kích thước từng part hoặc số luồng chạy song song (ví dụ để giới hạn tải lên hệ thống mạng nội bộ), dùng các lệnh multipart cấp thấp:

Python
1import boto3
2import math
3import os
4from concurrent.futures import ThreadPoolExecutor
5
6s3 = boto3.client(
7    service_name="s3",
8    endpoint_url="https://<ACCOUNT_ID>.r2.cloudflarestorage.com",
9    aws_access_key_id="<ACCESS_KEY_ID>",
10    aws_secret_access_key="<SECRET_ACCESS_KEY>",
11    region_name="auto",
12)
13
14bucket = "ten-bucket-cua-ban"
15key = "video-dataset.mp4"
16file_path = "./video-dataset.mp4"
17part_size = 16 * 1024 * 1024  # 16 MiB mỗi part
18max_workers = 6  # số luồng upload song song
19
20mpu = s3.create_multipart_upload(Bucket=bucket, Key=key)
21upload_id = mpu["UploadId"]
22
23def upload_part(part_number, data):
24    # Mỗi luồng tự upload một part, không phụ thuộc luồng khác
25    response = s3.upload_part(
26        Bucket=bucket, Key=key, UploadId=upload_id,
27        PartNumber=part_number, Body=data,
28    )
29    return {"PartNumber": part_number, "ETag": response["ETag"]}
30
31try:
32    file_size = os.path.getsize(file_path)
33    part_count = math.ceil(file_size / part_size)
34
35    with ThreadPoolExecutor(max_workers=max_workers) as pool:
36        futures = []
37        with open(file_path, "rb") as f:
38            for i in range(part_count):
39                data = f.read(part_size)
40                futures.append(pool.submit(upload_part, i + 1, data))
41        parts = [future.result() for future in futures]
42
43    s3.complete_multipart_upload(
44        Bucket=bucket, Key=key, UploadId=upload_id,
45        MultipartUpload={"Parts": parts},
46    )
47    print("Multipart upload hoàn tất.")
48except Exception:
49    # Hủy phiên dở dang nếu có lỗi, tránh tốn dung lượng lưu trữ oan
50    s3.abort_multipart_upload(Bucket=bucket, Key=key, UploadId=upload_id)
51    raise

6. Bảng so sánh các phương án xử lý upload file lớn

Phương án

Phù hợp khi nào

Độ phức tạp

Upload trực tiếp qua Worker (một lần, put())

File nhỏ đến vừa (dưới ~100MB), lượng truy cập thấp

Thấp

Multipart upload qua Worker (nhiều luồng song song)

File lớn (vài trăm MB đến vài GB, tối đa ~5TB), cần resume khi lỗi

Trung bình

Presigned URL upload thẳng lên R2

Lượng upload rất lớn/tần suất cao, muốn giảm tải hoàn toàn cho Worker

Trung bình–Cao

Upload qua S3 API (từ backend/script riêng)

Upload không xuất phát từ luồng xử lý của Worker (batch job, CI/CD, migrate dữ liệu)

Trung bình


VI. Một số pattern tích hợp phổ biến

Pattern

Mô tả ngắn

Độ phức tạp triển khai

Static asset serving

Worker đọc file từ R2 và trả về trực tiếp cho client, có thể kèm resize/transform

Thấp

Upload xử lý

Worker nhận file upload từ client, validate rồi ghi vào R2

Trung bình

Log aggregation

Worker ghi log request/event theo batch vào R2 để phân tích sau

Trung bình

Backup pipeline

Kết hợp Worker + Queues để đồng bộ dữ liệu định kỳ vào R2

Cao


VII. Lưu ý bảo mật và vận hành khi triển khai enterprise

1. Kiểm soát quyền truy cập binding theo môi trường

Không nên dùng chung một bucket cho nhiều môi trường (dev, staging, production). Việc tách bucket theo môi trường (như ví dụ ở mục IV.1.b) giúp giới hạn phạm vi ảnh hưởng nếu có sự cố cấu hình sai.

2. Giới hạn CPU time / kích thước object khi xử lý trong Worker

Worker có giới hạn về thời gian CPU xử lý mỗi request. Với các thao tác xử lý file lớn (resize ảnh, parse tài liệu) trực tiếp trong Worker, cần đánh giá kỹ giới hạn này để tránh timeout, đặc biệt với các gói có hạn mức CPU-time thấp hơn.

3. Giám sát và logging

Nên thiết lập logging riêng cho các thao tác ghi/xóa vào R2 (ví dụ ghi log ai/khi nào thao tác với object nào) để phục vụ audit, đặc biệt với dữ liệu nhạy cảm trong môi trường enterprise.


VIII. Câu hỏi thường gặp (FAQ)

1. Worker có thể truy cập nhiều bucket R2 cùng lúc không? Có. Bạn chỉ cần khai báo nhiều mục [[r2_buckets]] với các binding khác nhau trong cùng file wrangler.toml, mỗi binding trỏ đến một bucket riêng.

2. Binding R2 có tính phí riêng không, hay chỉ tính theo R2 pricing? Bản thân việc khai báo binding không phát sinh phí riêng. Chi phí vẫn tính theo dung lượng lưu trữ và số lượng operation (Class A/Class B) của R2, cộng với chi phí request/CPU-time của Worker nếu vượt hạn mức gói đang dùng.

3. Có cần expose R2 bucket public không khi dùng qua Worker? Không. Đây là một trong những lợi ích chính của binding — bucket có thể giữ ở chế độ private hoàn toàn, mọi truy cập đều đi qua logic kiểm soát trong code Worker thay vì URL công khai.

4. Khác biệt giữa dùng R2 binding và gọi R2 qua S3-compatible API là gì? Binding chỉ hoạt động trong runtime của Worker và không cần access key/secret key. Trong khi đó, S3-compatible API dùng để truy cập R2 từ bên ngoài hệ sinh thái Cloudflare (ví dụ từ một ứng dụng backend chạy trên server riêng), yêu cầu xác thực bằng credential kiểu S3.

5. File upload bị lỗi giữa chừng thì có phải upload lại từ đầu không? Nếu dùng multipart upload, không cần upload lại toàn bộ file — chỉ cần upload lại phần (part) bị lỗi, sau đó tiếp tục gọi hoàn tất (complete) như bình thường. Đây là lý do multipart upload phù hợp hơn cho file lớn so với cách upload một lần toàn bộ.


IX. Kết luận

Tích hợp Workers với R2 qua binding là cách hiệu quả để xây dựng ứng dụng edge có lưu trữ object mà không phát sinh egress fee, đồng thời giảm độ trễ nhờ compute và storage cùng chạy trên hạ tầng edge. Với doanh nghiệp đang cân nhắc các use case như xử lý upload, cache tài sản tĩnh hay pipeline log, đây là kiến trúc đáng để đánh giá kỹ trước khi triển khai ở quy mô production.

Bạn đang cân nhắc triển khai Workers kết nối R2 cho hệ thống của doanh nghiệp? Liên hệ đội ngũ tư vấn của chúng tôi để nhận đánh giá miễn phí và demo phù hợp với hạ tầng hiện tại.

Đăng ký tư vấn Cloudflare cho doanh nghiệp

Góc nhìn LionTech · Data & Applied AIXem các bài viết khác