file-storage
The file-storage package provides a key/value storage interface for File objects. It ships with two backends: a filesystem backend for writing files to disk and an in-memory backend for testing and development.
Installation
The file storage package is included with Remix. No additional installation is needed.
Import
import { FileStorage } from 'remix/file-storage'Backend-specific imports:
import { createFsFileStorage } from 'remix/file-storage/fs'
import { createMemoryFileStorage } from 'remix/file-storage/memory'API
FileStorage Interface
All file storage backends implement this interface:
interface FileStorage {
set(key: string, file: File): Promise<void>
get(key: string): Promise<File | null>
has(key: string): Promise<boolean>
remove(key: string): Promise<void>
}set(key, file)
Stores a file under the given key. If a file already exists at that key, it is overwritten.
await storage.set('avatars/user-123.jpg', file)get(key)
Retrieves a file by key. Returns null if no file exists at that key.
let file = await storage.get('avatars/user-123.jpg')
if (file) {
console.log(file.name) // 'user-123.jpg'
console.log(file.type) // 'image/jpeg'
console.log(file.size) // 1048576
}has(key)
Returns true if a file exists at the given key.
if (await storage.has('avatars/user-123.jpg')) {
// File exists
}remove(key)
Deletes the file at the given key. No error is thrown if the key does not exist.
await storage.remove('avatars/user-123.jpg')Backends
createFsFileStorage(options)
Creates a file storage backend that reads and writes files to the local filesystem.
import { createFsFileStorage } from 'remix/file-storage/fs'
let storage = createFsFileStorage({
directory: './uploads',
})| Option | Type | Description |
|---|---|---|
directory | string | The root directory for stored files. Created automatically if it does not exist. |
The key maps directly to the file path within the directory. For example, storage.set('photos/cat.jpg', file) writes to ./uploads/photos/cat.jpg.
createMemoryFileStorage()
Creates an in-memory file storage backend. Useful for testing and development.
import { createMemoryFileStorage } from 'remix/file-storage/memory'
let storage = createMemoryFileStorage()All files are stored in memory and lost when the process exits.
Examples
Upload Handler with Filesystem Storage
import { parseFormData } from 'remix/form-data-parser'
import { createFsFileStorage } from 'remix/file-storage/fs'
let storage = createFsFileStorage({ directory: './uploads' })
router.map(uploadRoute, async ({ request }) => {
let formData = await parseFormData(request, {}, async (fileUpload) => {
let key = `${Date.now()}-${fileUpload.name}`
await storage.set(key, fileUpload)
return key
})
let fileKey = formData.get('file') as string
return new Response(`Stored as ${fileKey}`)
})Serving Stored Files
import { createFsFileStorage } from 'remix/file-storage/fs'
import { createFileResponse } from 'remix/response/file'
let storage = createFsFileStorage({ directory: './uploads' })
router.map(fileRoute, async ({ request, params }) => {
let file = await storage.get(params.key)
if (!file) {
return new Response('Not Found', { status: 404 })
}
return createFileResponse(file, request)
})Testing with In-Memory Storage
import { describe, it } from 'remix/test'
import { createMemoryFileStorage } from 'remix/file-storage/memory'
describe('file upload', () => {
it('stores a file', async (t) => {
let storage = createMemoryFileStorage()
let file = new File(['hello'], 'test.txt', { type: 'text/plain' })
await storage.set('test.txt', file)
t.assert.ok(await storage.has('test.txt'))
let retrieved = await storage.get('test.txt')
t.assert.equal(await retrieved!.text(), 'hello')
})
})Related
- file-storage-s3 --- S3-compatible file storage backend.
- form-data-parser --- Parse form data with file uploads.
- File Uploads Guide --- End-to-end guide to file uploads in Remix.