How Appwrite Storage API Handles File Management and Manipulation
Appwrite's storage API implements a bucket-based architecture where files are treated as first-class resources stored in dedicated database collections, supporting chunked uploads, compression, encryption, and granular permissions through a device abstraction layer compatible with local disk, S3, Azure, and GCS backends.
The Appwrite storage API provides a comprehensive backend-as-a-service solution for handling file operations in web and mobile applications. As implemented in the appwrite/appwrite repository, this system abstracts physical storage complexities through a sophisticated metadata layer and a pluggable device architecture that supports multiple cloud providers.
Bucket-Based Architecture and Metadata Storage
Appwrite organizes files into buckets, which function as logical containers similar to collections in the Database service. When you create a bucket via POST /v1/storage/buckets, the controller in app/controllers/api/storage.php (lines 59-71) persists it as a document in the buckets collection using $dbForProject->createDocument('buckets', …).
Each bucket creation triggers the generation of a dedicated collection named bucket_<sequence> to store file metadata documents (lines 52-55). Buckets define comprehensive policies including maximum file size, allowed extensions, compression (GZIP/ZSTD), encryption, antivirus scanning, and whether file-level permissions are enabled.
File Lifecycle Operations
Upload and Chunked Transfer
File creation uses POST /v1/storage/buckets/:bucketId/files, parsing multipart/form-data in app/controllers/api/storage.php (lines 99-110). The controller validates size, extension, and permissions before calling $deviceForFiles->upload(...) to write the binary to a path derived from the bucket ID.
For large files, the API supports chunked uploads using the Content-Range header. The Web SDK automatically splits files into 5MB chunks, sending each with progress tracking. The server appends each chunk via $deviceForFiles->upload(...) until chunksUploaded === chunksTotal, with abort capabilities via $deviceForFiles->abort(...) for incomplete transfers.
Download, View, and Preview
The API provides three distinct access patterns. Download (GET .../download) returns signed URLs with Content-Disposition: attachment headers. View (GET .../view) omits the attachment header for inline browser display.
Preview (GET .../preview) validates file types and uses ImageMagick via Imagick to apply transformations (resize, crop, quality, borders) before streaming, as implemented in app/controllers/api/storage.php (lines 947-962).
Metadata Updates and Deletion
Updates use PUT /v1/storage/buckets/:bucketId/files/:fileId to modify document fields like name and $permissions without altering file content. Deletion via DELETE .../files/:fileId checks permissions, removes the physical file via $deviceForFiles->delete(...) (or abort for incomplete uploads), and deletes the document from the bucket collection (lines 1755-1785). This triggers events and cache purging.
Security Model and Permission Aggregation
Appwrite implements a two-tier permission system. Bucket-level permissions (read, write, delete) are evaluated first on every request. When file-security is enabled on the bucket, the controller also validates individual file permissions using Authorization::isValid(new Input(Database::PERMISSION_*, $file->get*())).
Permission aggregation converts high-level permission strings into full permission lists via Permission::aggregate (lines 53-56), allowing complex access control scenarios while maintaining simple API interfaces.
Event-Driven Architecture and Cache Management
Every write operation fires specific events (e.g., buckets.[bucketId].files.[fileId].delete) queued via $queueForEvents->setParam… for webhooks, real-time updates, and audit logging (lines 1759-1772).
Delete operations specifically enqueue cache-purge tasks using DELETE_TYPE_CACHE_BY_RESOURCE to invalidate cached previews and downloads (lines 1829-1834), ensuring consistency across distributed systems.
SDK Abstractions and Client-Side Handling
The Web SDK in public/sdk-web/services/storage.ts provides client-side conveniences including automatic chunked upload logic that handles Content-Range headers and progress callbacks. It exposes helper methods getFileDownload, getFileView, and getFilePreview to construct properly authenticated URLs with project IDs and transformation parameters (lines 72-92).
Storage Backends and Extensibility
The API uses the Utopia\Storage\Device abstraction to support multiple backends—local disk, Amazon S3, Azure Blob Storage, and Google Cloud Storage—without modifying controller logic. Compression algorithms like GZIP and ZSTD are implemented in src/Utopia/Storage/Compression/Algorithms/, while extension validation uses src/Utopia/Storage/Validator/FileExt.php.
New bucket attributes can be added without API changes because the controller reads configuration from the bucket document at runtime, enabling extensible policy enforcement.
Practical Implementation Examples
Create a Bucket with Security Policies
$bucket = $client->storage->createBucket(
bucketId: ID::unique(),
name: 'avatars',
fileSecurity: true,
allowedFileExtensions: ['jpg', 'png'],
maximumFileSize: 5 * 1024 * 1024, // 5 MB
compression: Compression::GZIP,
encryption: true,
antivirus: true
);
Underlying call: POST /v1/storage/buckets handled in app/controllers/api/storage.php.
Upload a Large File with Automatic Chunking
import { Client, Storage, ID } from 'appwrite';
const client = new Client()
.setEndpoint('https://[HOSTNAME_OR_IP]/v1')
.setProject('[PROJECT_ID]');
const storage = new Storage(client);
await storage.createFile(
'bucketId123',
ID.unique(),
largeFile, // File object from <input type="file">
['read("any")'], // Public read permission
(progress) => console.log(progress) // Progress callback
);
The SDK automatically splits the file into 5MB chunks, sends each with Content-Range headers, and assembles the final file on the server.
Delete a File via REST API
curl -X DELETE "https://[HOST]/v1/storage/buckets/bucketId123/files/fileId456" \
-H "X-Appwrite-Project: yourProjectId" \
-H "X-Appwrite-Key: yourSecretKey"
Calls DELETE /v1/storage/buckets/:bucketId/files/:fileId → logic in app/controllers/api/storage.php lines 1755-1790.
Generate an Image Preview with Transformations
const previewUrl = storage.getFilePreview(
'bucketId123',
'fileId456',
200, // width
200, // height
'center', // gravity
80, // quality
2, // border width
'ff0000', // border color
10, // border radius
undefined,
0,
undefined,
'webp' // output format
);
window.open(previewUrl);
The SDK constructs a URL with query parameters consumed by the preview endpoint, applying ImageMagick transformations server-side.
Summary
- Appwrite's storage API organizes files into buckets that act as logical containers with configurable policies for size limits, extensions, compression, and encryption.
- Files are uploaded via multipart/form-data or chunked transfers (5MB chunks), stored physically through the Device abstraction (supporting local, S3, Azure, GCS), and tracked as documents in dedicated bucket collections.
- The permission model evaluates bucket-level rules first, optionally checking file-level permissions when file-security is enabled, using
Permission::aggregatefor complex access control. - The API provides preview, download, and view endpoints, with previews supporting ImageMagick transformations for image manipulation.
- Event-driven architecture ensures real-time updates, webhooks, and cache invalidation occur automatically on file mutations through queued events and
DELETE_TYPE_CACHE_BY_RESOURCEtasks.
Frequently Asked Questions
What is the maximum file size supported by Appwrite's storage API?
The maximum file size is configured per bucket via the maximumFileSize attribute, allowing project-specific limits rather than global restrictions. The API supports chunked uploads to handle large files efficiently, with the Web SDK automatically splitting files into 5MB chunks and transmitting them with Content-Range headers.
How does Appwrite handle file security and access control?
Appwrite implements a two-tier permission system. Bucket-level permissions (read, write, delete) are evaluated first for all operations. If file-security is enabled on the bucket, the API also validates individual file permissions using Authorization::isValid(), allowing granular access control while maintaining bucket-wide defaults through Permission::aggregate.
Can Appwrite storage work with cloud providers like AWS S3?
Yes, through the Utopia\Storage\Device abstraction layer. The storage API supports multiple backends including local disk, Amazon S3, Azure Blob Storage, and Google Cloud Storage without requiring changes to the API logic or client code, enabling seamless migration between storage providers.
What happens when a file is deleted in Appwrite?
Deletion triggers a cascade of operations defined in app/controllers/api/storage.php (lines 1755-1785): the physical file is removed via $deviceForFiles->delete(), the metadata document is deleted from the bucket collection, an event buckets.[bucketId].files.[fileId].delete is queued for webhooks and real-time updates, and cache invalidation tasks (DELETE_TYPE_CACHE_BY_RESOURCE) are enqueued to clear cached previews.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →