file-storage-s3
The file-storage-s3 package provides an S3-compatible file storage backend that implements the FileStorage interface. It works with Amazon S3, Cloudflare R2, MinIO, and any other service that implements the S3 API.
Installation
The S3 file storage backend is included with Remix. No additional installation is needed.
Import
ts
import { createS3FileStorage } from 'remix/file-storage-s3'API
createS3FileStorage(options)
Creates a file storage backend that reads and writes files to an S3-compatible object store.
ts
function createS3FileStorage(options: S3FileStorageOptions): FileStorageThe returned object implements the full FileStorage interface: set, get, has, and remove.
Options
| Option | Type | Required | Description |
|---|---|---|---|
bucket | string | Yes | The S3 bucket name. |
region | string | Yes | The AWS region (e.g. 'us-east-1'). |
credentials | object | No | AWS credentials. Uses the default credential chain if omitted. |
credentials.accessKeyId | string | -- | AWS access key ID. |
credentials.secretAccessKey | string | -- | AWS secret access key. |
endpoint | string | No | Custom endpoint URL for S3-compatible services. |
forcePathStyle | boolean | No | Use path-style URLs instead of virtual-hosted-style. Required for some S3-compatible services. |
prefix | string | No | A prefix prepended to all keys. Useful for organizing files within a bucket. |
Examples
Amazon S3
ts
import { createS3FileStorage } from 'remix/file-storage-s3'
let storage = createS3FileStorage({
bucket: 'my-app-uploads',
region: 'us-east-1',
credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
},
})
// Store a file
await storage.set('avatars/user-123.jpg', file)
// Retrieve a file
let file = await storage.get('avatars/user-123.jpg')
// Check existence
let exists = await storage.has('avatars/user-123.jpg')
// Delete a file
await storage.remove('avatars/user-123.jpg')Cloudflare R2
ts
let storage = createS3FileStorage({
bucket: 'my-bucket',
region: 'auto',
endpoint: 'https://ACCOUNT_ID.r2.cloudflarestorage.com',
credentials: {
accessKeyId: process.env.R2_ACCESS_KEY_ID!,
secretAccessKey: process.env.R2_SECRET_ACCESS_KEY!,
},
})MinIO
ts
let storage = createS3FileStorage({
bucket: 'my-bucket',
region: 'us-east-1',
endpoint: 'http://localhost:9000',
forcePathStyle: true,
credentials: {
accessKeyId: 'minioadmin',
secretAccessKey: 'minioadmin',
},
})With a Key Prefix
Organize files within a shared bucket:
ts
let avatars = createS3FileStorage({
bucket: 'my-app',
region: 'us-east-1',
prefix: 'avatars/',
})
let documents = createS3FileStorage({
bucket: 'my-app',
region: 'us-east-1',
prefix: 'documents/',
})
// Writes to s3://my-app/avatars/user-123.jpg
await avatars.set('user-123.jpg', file)
// Writes to s3://my-app/documents/report.pdf
await documents.set('report.pdf', file)Upload Handler
Use with parseFormData to stream uploads directly to S3:
ts
import { parseFormData } from 'remix/form-data-parser'
import { createS3FileStorage } from 'remix/file-storage-s3'
let storage = createS3FileStorage({
bucket: 'my-uploads',
region: 'us-east-1',
})
router.map(uploadRoute, async ({ request }) => {
let formData = await parseFormData(request, {}, async (fileUpload) => {
let key = `uploads/${Date.now()}-${fileUpload.name}`
await storage.set(key, fileUpload)
return key
})
let fileKey = formData.get('file') as string
return Response.json({ key: fileKey })
})Related
- file-storage --- The FileStorage interface and built-in backends.
- form-data-parser --- Parse form data with file uploads.
- File Uploads Guide --- End-to-end guide to file uploads in Remix.