Skip to content
workers-go
Esc
navigateopen⌘Jpreview
On this page

R2

Store and serve objects in a Cloudflare R2 bucket with Head, Get, Put, Delete, and List.

Cloudflare R2 is S3-compatible object storage. The cloudflare/r2 package wraps a bound bucket.

Declare the binding

[[r2_buckets]]
binding = "BUCKET"
bucket_name = "my-bucket"
preview_bucket_name = "my-bucket-dev"

Connect to a bucket

bucket, err := r2.NewBucket("BUCKET")
if err != nil {
	// no binding named BUCKET
}

Get an object

Get returns an *r2.Object whose Body (an io.ReadCloser) streams the contents. It returns nil (with no error) when the key does not exist. Head returns the metadata only — its Body is always nil.

obj, err := bucket.Get("images/logo.png")
if err != nil {
	return err
}
if obj == nil {
	// not found
}
defer obj.Body.Close()
io.Copy(w, obj.Body)

An *r2.Object exposes Key, Version, Size, ETag, HTTPETag, Uploaded, HTTPMetadata, CustomMetadata, and Body.

Put an object

Put stores an object from an io.ReadCloser. The body is copied into memory before upload.

_, err = bucket.Put("images/logo.png", req.Body, &r2.PutOptions{
	HTTPMetadata: r2.HTTPMetadata{
		ContentType:  req.Header.Get("Content-Type"),
		CacheControl: "public, max-age=14400",
	},
	CustomMetadata: map[string]string{"custom-key": "custom-value"},
})

PutOptions fields: HTTPMetadata (ContentType, ContentLanguage, ContentDisposition, ContentEncoding, CacheControl, CacheExpiry), CustomMetadata, and MD5.

Delete an object

err := bucket.Delete("images/logo.png")

List objects

objects, err := bucket.List()
for _, obj := range objects.Objects {
	fmt.Println(obj.Key, obj.Size)
}

Multipart upload

For large objects, use a multipart upload to upload parts individually instead of buffering the whole object in memory. CreateMultipartUpload starts one; UploadPart sends each part (numbered 1 to 10,000) from an io.Reader; Complete assembles the uploaded parts, in order, into the final object.

upload, err := bucket.CreateMultipartUpload("videos/large.mp4", &r2.MultipartOptions{
	HTTPMetadata: r2.HTTPMetadata{ContentType: "video/mp4"},
})
if err != nil {
	return err
}

var parts []r2.UploadedPart
for i, chunk := range chunks {
	part, err := upload.UploadPart(i+1, bytes.NewReader(chunk))
	if err != nil {
		// abort on failure to avoid leaving an incomplete upload behind
		upload.Abort()
		return err
	}
	parts = append(parts, *part)
}

obj, err := upload.Complete(parts)

ResumeMultipartUpload(key, uploadID) returns a handle to an existing multipart upload without validating that it exists on the server — a mistaken key/uploadID surfaces as an error from the first UploadPart, Complete, or Abort call instead. Abort cancels an in-progress upload. MultipartOptions accepts the same HTTPMetadata, CustomMetadata, and additionally StorageClass fields as PutOptions.

Example projects

Was this page helpful?