Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ Each built-in secret also has a dedicated environment variable: `SERVERPOD_DATAB

For secrets related to first-party Serverpod packages, see their respective documentation:

- **Cloud storage**: see [Uploading files](../endpoints-and-apis/file-uploads) for Google Cloud Storage, AWS S3, and Cloudflare R2 secrets.
- **Cloud storage**: see [File uploads](../endpoints-and-apis/file-uploads) for Google Cloud Storage, AWS S3, and Cloudflare R2 secrets, and [Custom cloud storage](../endpoints-and-apis/custom-cloud-storage) for local disk.
- **Authentication**: see [Storing Secrets](../authentication/setup#storing-secrets) on the Authentication setup page.

### Custom secrets
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ The generated test tools call your endpoints the same way production code does,
- [Error handling and exceptions](./endpoints-and-apis/error-handling-and-exceptions): typed errors between server and app.
- [Streaming](./endpoints-and-apis/streaming): push live data to your app.
- [File uploads](./endpoints-and-apis/file-uploads): direct-to-storage uploads.
- [Custom cloud storage](./endpoints-and-apis/custom-cloud-storage): local disk, NAS, and custom backends.
- [Endpoint inheritance](./endpoints-and-apis/endpoint-inheritance): share behavior across endpoints and reshape module endpoints.
- [Server events](./endpoints-and-apis/server-events): publish and subscribe to messages across sessions and servers.
- [Endpoint middleware](./endpoints-and-apis/endpoint-middleware): wrap every API request for logging or rate limiting.
Expand Down
22 changes: 18 additions & 4 deletions docs/06-concepts/02-endpoints-and-apis/07-file-uploads.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ description: File uploads in Serverpod go directly to storage via signed upload

# File uploads

Let your users upload avatars, documents, or any other files. The app sends the file straight to storage instead of through your endpoint methods, which keeps large files out of your API calls. Out of the box, your server stores files in the database, which works well for development. In production, configure Google Cloud Storage, AWS S3, or Cloudflare R2 instead.
Let your users upload avatars, documents, or any other files. The app sends the file straight to storage instead of through your endpoint methods, which keeps large files out of your API calls. Out of the box, your server stores files in the database, which works well for development. In production, configure Google Cloud Storage, AWS S3, or Cloudflare R2 instead. To keep files on local disk or a NAS, see [Custom cloud storage](./custom-cloud-storage).

## Upload a file

Expand All @@ -26,7 +26,7 @@ Future<String> getUploadDescription(Session session, String path) async {
The `createUploadDescription` method also accepts an optional `UploadOptions` object to control the upload:

- **`UploadOptions.expirationDuration`**: How long the upload URL is valid. Defaults to 10 minutes.
- **`UploadOptions.maxFileSize`**: Maximum allowed file size in bytes. Defaults to 10 MB.
- **`UploadOptions.maxFileSize`**: Maximum allowed file size in bytes. Defaults to 10 MiB.
- **`UploadOptions.contentLength`**: The exact file size in bytes. When provided, the storage provider validates the upload size against `maxFileSize`.
- **`UploadOptions.preventOverwrite`**: When `true`, the upload will fail if a file already exists at the given path. Defaults to `false`.
- **`UploadOptions.metadata`**: HTTP metadata and custom key-value data to store with the file.
Expand Down Expand Up @@ -71,7 +71,7 @@ Future<bool> verifyUpload(Session session, String path) async {

### Client-side code

To upload a file from the app side, first request the upload description. Next, upload the file, from either a `Stream` or a `ByteData` object. When uploading from a `Stream`, pass the file length if you know it: without a length, a multipart upload buffers the whole file in memory. The uploader does not report upload progress. Finally, verify the upload with the server.
To upload a file from the app side, first request the upload description. Next, upload the file, from either a `Stream` or a `ByteData` object. When uploading from a `Stream`, pass the file length if you know it: without a length, a multipart upload buffers the whole file in memory. The uploader does not report upload progress. On failure it returns `false` with no status code and no exception, so a network error and a file that is too large look the same. Finally, verify the upload with the server.

```dart
final uploadDescription = await client.myEndpoint.getUploadDescription('myfile');
Expand All @@ -90,6 +90,19 @@ In a real-world app, you most likely want to create the file paths on your serve

:::

## Size limits

When the app uploads through your API server (the default database storage), two size limits apply. The upload uses the smaller of the two.

| Limit | Default | Set in |
| --- | --- | --- |
| `maxRequestSize` | 524288 (512 KiB) | `config/<run-mode>.yaml` or `SERVERPOD_MAX_REQUEST_SIZE` |
| `UploadOptions.maxFileSize` | 10 MiB | `createUploadDescription` |

A 5 MB file with default config is rejected even though `maxFileSize` is 10 MiB. Raise `maxRequestSize` in every run-mode YAML you use to at least the largest file you accept. See the [Configuration reference](../lookups/configuration-reference).

Uploads to S3, Google Cloud Storage, and R2 go to the provider, so `maxRequestSize` does not apply to the file body.

## Access stored files

You can check if a file exists or retrieve it directly from your server. Files in public storage are also accessible via URL.
Expand Down Expand Up @@ -179,7 +192,7 @@ To delete a stored file, use `deleteFile` with the same `storageId` and `path`.

## Configure a storage provider

Each storage is identified by a `storageId`. Serverpod comes with two default storages, `public` and `private`. Replace these with a cloud-backed implementation, or add additional storages with custom IDs. Call `pod.addCloudStorage()` before `pod.start()`.
Each storage is identified by a `storageId`. Serverpod comes with two default storages, `public` and `private`. Replace these with a cloud-backed implementation, or add additional storages with custom IDs. Calling `pod.addCloudStorage` with `public` or `private` replaces that default. Call it before `pod.start()`. For local disk or a NAS, see [Custom cloud storage](./custom-cloud-storage).

Pick the package that matches your provider. Use [serverpod_cloud_storage_s3](https://pub.dev/packages/serverpod_cloud_storage_s3) for AWS S3, [serverpod_cloud_storage_gcp](https://pub.dev/packages/serverpod_cloud_storage_gcp) for Google Cloud Storage, or [serverpod_cloud_storage_r2](https://pub.dev/packages/serverpod_cloud_storage_r2) for Cloudflare R2.

Expand Down Expand Up @@ -387,5 +400,6 @@ pod.addCloudStorage(

## Related

- [Custom cloud storage](./custom-cloud-storage): local disk, NAS, and implementing `CloudStorage`.
- [Configuration](../server-fundamentals/configuration): passwords file and environment variables for storage keys.
- [Sessions](./sessions): the `storage` member used in the examples above.
Loading
Loading