Storage
Learn how to set up and use cloud storage for file uploads and media handling
MkSaaS uses Amazon S3 and compatible services like Cloudflare R2 for file storage and media handling, providing a reliable and scalable solution for storing images and other media files.
Setup
To set up storage in MkSaaS, follow these steps to configure the necessary environment variables:
Cloudflare R2 (Recommended)
- Create a Cloudflare account at cloudflare.com
- Create a new R2 bucket:
- Pick a globally unique bucket name (e.g.,
your-project-name
) - Select a region close to your target audience
- Set other options according to your needs
- Pick a globally unique bucket name (e.g.,
- Allow public access to the bucket:
Settings
>Public Development URL
, clickEnable
- Save the public access URL as
STORAGE_PUBLIC_URL
- Set custom domains for public access to the bucket
- Create a new API Token:
R2
>API
>Manage API Tokens
, clickCreate User API Token
- Set permissions to
Object Read & Write
to the bucket - Create the API Token, get the
Access Key ID
andSecret Access Key
- Set the following environment variables:
STORAGE_REGION=your-region-or-auto
STORAGE_BUCKET_NAME=your-bucket-name
STORAGE_ACCESS_KEY_ID=your-access-key
STORAGE_SECRET_ACCESS_KEY=your-secret-key
STORAGE_ENDPOINT=https://xxx.r2.cloudflarestorage.com
STORAGE_PUBLIC_URL=https://pub-xxx.r2.dev
Set storage provider to s3
because Cloudflare R2 is S3-compatible.
Amazon S3
-
Create an AWS account at aws.amazon.com
-
Create a new S3 bucket:
- Pick a globally unique bucket name (e.g.,
your-project-name
) - Select a region close to your target audience
- Disable
Block all public access
if you want files to be publicly accessible - Set other options according to your needs
- Pick a globally unique bucket name (e.g.,
-
Add a bucket policy to allow public access. Go to the
Permissions
tab and add:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadGetObject",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::your-bucket-name/*"
}
]
}
-
Create an IAM user with S3 access:
- Navigate to the IAM service
- Create a new policy with S3 permissions:
GetObject
,PutObject
,DeleteObject
,ListBucket
- Create a new IAM user with
Programmatic access
- Attach the policy you created
- Save the Access Key ID and Secret Access Key
-
Add the following environment variables:
STORAGE_REGION=your-region
STORAGE_BUCKET_NAME=your-bucket-name
STORAGE_ACCESS_KEY_ID=your-access-key
STORAGE_SECRET_ACCESS_KEY=your-secret-key
S3-Compatible Alternatives
MkSaaS is compatible with any S3-compatible storage service, including:
- Backblaze B2 - Very cost-effective option
- Google Cloud Storage - High performance with Google infrastructure
- Digital Ocean Spaces - Simple pricing and integrated with DO infrastructure
If you are setting up the environment, now you can go back to the Environment Setup guide and continue. The rest of this guide can be read later.
Environment Setup
Set up environment variables
Storage System Structure
The storage system in MkSaaS is designed with the following components:
This modular structure makes it easy to extend the storage system with new providers and functionality.
Core Features
- Direct file upload and management for server-side operations
- Browser-side file uploading with size-based optimization
- Support for Amazon S3 and compatible storage services (e.g. Cloudflare R2)
- Folder organization for file management
- Automatic file path generation and file naming
- Configurable storage region and bucket settings
Usage
Basic File Operations
MkSaaS provides simple utilities for common file operations:
import { uploadFile, deleteFile } from '@/storage';
// Upload a file to storage
const { url, key } = await uploadFile(
fileBuffer,
'original-filename.jpg',
'image/jpeg',
'uploads/images'
);
// Delete a file from storage
await deleteFile(key);
Browser-Side Uploads
For uploading files directly from the browser, use the uploadFileFromBrowser
function:
'use client';
import { uploadFileFromBrowser } from '@/storage/client';
// In your React component
async function handleFileUpload(event) {
const file = event.target.files[0];
try {
const { url, key } = await uploadFileFromBrowser(file, 'uploads/images');
console.log('File uploaded:', url);
} catch (error) {
console.error('Upload failed:', error);
}
}
Using with Form Components
Below is an example of using the storage system with a form component:
'use client';
import { useState } from 'react';
import { uploadFileFromBrowser } from '@/storage/client';
import { Button } from '@/components/ui/button';
export function ImageUploader() {
const [isUploading, setIsUploading] = useState(false);
const [imageUrl, setImageUrl] = useState<string | null>(null);
async function handleFileChange(event: React.ChangeEvent<HTMLInputElement>) {
const file = event.target.files?.[0];
if (!file) return;
try {
setIsUploading(true);
const { url } = await uploadFileFromBrowser(file, 'profile-images');
setImageUrl(url);
} catch (error) {
console.error('Upload failed:', error);
} finally {
setIsUploading(false);
}
}
return (
<div className="space-y-4">
<div className="flex items-center gap-4">
<Button
variant="outline"
onClick={() => document.getElementById('file-upload')?.click()}
disabled={isUploading}
>
{isUploading ? 'Uploading...' : 'Upload Image'}
</Button>
<input
id="file-upload"
type="file"
accept="image/*"
className="hidden"
onChange={handleFileChange}
/>
</div>
{imageUrl && (
<div className="w-32 h-32 relative">
<img
src={imageUrl}
alt="Uploaded image"
className="object-cover w-full h-full rounded-md"
/>
</div>
)}
</div>
);
}
Customization
Creating a Custom Provider
MkSaaS makes it easy to extend the storage system with new storage service providers:
-
Create a new file in the
src/storage/provider
directory as a custom storage service provider -
Implement the
StorageProvider
interface
import {
PresignedUploadUrlParams,
StorageProvider,
UploadFileParams,
UploadFileResult
} from '@/storage/types';
export class CustomStorageProvider implements StorageProvider {
constructor() {
// Initialize your storage service provider
}
public getProviderName(): string {
return 'CustomProvider';
}
async uploadFile(params: UploadFileParams): Promise<UploadFileResult> {
// Implementation for uploading a file
return { url: 'https://example.com/file.jpg', key: 'file.jpg' };
}
async deleteFile(key: string): Promise<void> {
// Implementation for deleting a file
}
}
- Update the storage service provider selection in
index.ts
:
import { CustomStorageProvider } from './provider/custom-provider';
export const initializeStorageProvider = (): StorageProvider => {
if (!storageProvider) {
if (websiteConfig.storage.provider === 's3') {
storageProvider = new S3Provider();
} else if (websiteConfig.storage.provider === 'custom') {
storageProvider = new CustomStorageProvider();
} else {
throw new Error(
`Unsupported storage provider: ${websiteConfig.storage.provider}`
);
}
}
return storageProvider;
};
Best Practices
- File Organization: Use folders to organize files by type or purpose (e.g.,
uploads/images
,documents/contracts
) - File Size Limits: Set reasonable file size limits to prevent abuse
- File Type Validation: Validate file types on both client and server sides for security
Video Tutorial
Next Steps
Now that you understand how to work with file storage in MkSaaS, explore these related topics: