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
- _examples/r2-image-server — upload and download images.
- _examples/r2-image-viewer — serve stored images.