Skip to main content

Overview

Ecom supports multiple storage drivers for managing uploaded files including product images, user avatars, and digital downloads. Choose between local storage or cloud storage providers for scalability and reliability.

Supported Storage Drivers

  • Local: Store files on your server
  • AWS S3: Amazon Simple Storage Service
  • Backblaze B2: Cost-effective S3-compatible storage
  • Public: Laravel’s public disk

Configuration

Config File Location

config/filesystems.php

Filesystem Configuration

config/filesystems.php
return [
    'default' => env('FILESYSTEM_DRIVER', 'local'),
    
    'cloud' => env('FILESYSTEM_CLOUD', 's3'),
    
    'disks' => [
        'local' => [
            'driver' => 'local',
            'root' => public_path(),
        ],

        'public' => [
            'driver' => 'local',
            'root' => storage_path('app/public'),
            'url' => env('APP_URL').'/storage',
            'visibility' => 'public',
        ],

        'aws' => [
            'driver'    => 's3',
            'key'       => env('AWS_ACCESS_KEY_ID'),
            'secret'    => env('AWS_SECRET_ACCESS_KEY'),
            'region'    => env('AWS_DEFAULT_REGION'),
            'bucket'    => env('AWS_BUCKET'),
            'url'       => env('AWS_URL'),
        ],

        'backblaze' => [
            'driver'    => 's3',
            'key'       => env('BACKBLAZE_ACCESS_KEY_ID'),
            'secret'    => env('BACKBLAZE_SECRET_ACCESS_KEY'),
            'region'    => env('BACKBLAZE_DEFAULT_REGION'),
            'bucket'    => env('BACKBLAZE_BUCKET'),
            'url'       => env('BACKBLAZE_URL'),
            'endpoint'  => env('BACKBLAZE_ENDPOINT'),
        ],
    ],
];

Local Storage

Configuration

Default configuration stores files in public directory:
.env
FILESYSTEM_DRIVER=local

Benefits

  • No additional costs
  • Simple setup
  • Fast access
  • Full control

Limitations

  • Limited scalability
  • No automatic backups
  • Server disk space constraints
  • No CDN integration
  • Not suitable for multiple servers

Usage

// Store file locally
$path = $request->file('image')->store('uploads', 'local');

// Access file
$url = asset('uploads/' . $filename);

AWS S3 Configuration

Amazon S3 is the industry-standard cloud storage solution.

Step 1: Create AWS Account

1

Sign Up for AWS

Go to AWS Console and create account
2

Create IAM User

Navigate to IAM → Users → Add User
  • User name: ecom-storage
  • Access type: Programmatic access
3

Attach Policy

Attach AmazonS3FullAccess policy or create custom policy:
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": ["s3:*"],
      "Resource": [
        "arn:aws:s3:::your-bucket-name",
        "arn:aws:s3:::your-bucket-name/*"
      ]
    }
  ]
}
4

Save Credentials

Download CSV with Access Key ID and Secret Access Key
5

Create S3 Bucket

Navigate to S3 → Create bucket
  • Bucket name: your-unique-bucket-name
  • Region: Select closest to your users
  • Unblock public access (for public files)
6

Configure CORS

Add CORS configuration to bucket:
[
  {
    "AllowedHeaders": ["*"],
    "AllowedMethods": ["GET", "PUT", "POST", "DELETE"],
    "AllowedOrigins": ["*"],
    "ExposeHeaders": []
  }
]

Step 2: Configure Environment

.env
# AWS S3 Configuration
FILESYSTEM_DRIVER=s3
AWS_ACCESS_KEY_ID=AKIA...
AWS_SECRET_ACCESS_KEY=...
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=your-bucket-name
AWS_URL=https://your-bucket-name.s3.amazonaws.com

Step 3: Install AWS SDK

AWS SDK is already included:
composer.json
"require": {
    "league/flysystem-aws-s3-v3": "^3.12"
}
If not installed:
composer require league/flysystem-aws-s3-v3

S3 Regions

Common AWS regions:
Region CodeRegion Name
us-east-1US East (N. Virginia)
us-west-2US West (Oregon)
eu-west-1Europe (Ireland)
eu-central-1Europe (Frankfurt)
ap-south-1Asia Pacific (Mumbai)
ap-southeast-1Asia Pacific (Singapore)

Usage Example

// Upload to S3
$path = Storage::disk('s3')->put('products', $request->file('image'));

// Get URL
$url = Storage::disk('s3')->url($path);

// Delete from S3
Storage::disk('s3')->delete($path);

Backblaze B2 Configuration

Backblaze B2 is a cost-effective alternative to AWS S3 with S3-compatible API.

Pricing Comparison

StorageAWS S3Backblaze B2
Storage$0.023/GB$0.005/GB
Download$0.09/GB$0.01/GB (free first 3x storage)
API CallsVaries2,500 free daily

Step 1: Create Backblaze Account

1

Sign Up

2

Create Bucket

Navigate to B2 Cloud StorageCreate a Bucket
  • Bucket name: your-bucket-name
  • Files in Bucket: Public
  • Encryption: Disable (or enable if needed)
3

Create Application Key

Go to App KeysAdd a New Application Key
  • Key Name: ecom-storage
  • Allow access to: your-bucket-name
  • Permissions: Read and Write
4

Save Credentials

Copy:
  • keyID (Access Key)
  • applicationKey (Secret Key)
  • Endpoint URL

Step 2: Configure Environment

.env
# Backblaze B2 Configuration
FILESYSTEM_DRIVER=backblaze
BACKBLAZE_ACCESS_KEY_ID=your_key_id
BACKBLAZE_SECRET_ACCESS_KEY=your_application_key
BACKBLAZE_DEFAULT_REGION=us-west-002
BACKBLAZE_BUCKET=your-bucket-name
BACKBLAZE_URL=https://f002.backblazeb2.com/file/your-bucket-name
BACKBLAZE_ENDPOINT=https://s3.us-west-002.backblazeb2.com

Backblaze Regions

Region CodeRegion Name
us-west-001US West
us-west-002US West 2
eu-central-003Europe

Usage Example

// Upload to Backblaze
$path = Storage::disk('backblaze')->put('products', $request->file('image'));

// Get URL
$url = Storage::disk('backblaze')->url($path);

// Delete from Backblaze
Storage::disk('backblaze')->delete($path);

File Upload Implementation

Upload Helper Function

Ecom includes upload helper in:
app/Http/Helpers.php

Example Upload Controller

public function uploadImage(Request $request)
{
    $request->validate([
        'image' => 'required|image|max:2048', // 2MB max
    ]);

    // Get configured disk
    $disk = env('FILESYSTEM_DRIVER', 'local');

    if ($disk == 's3' || $disk == 'backblaze') {
        // Upload to cloud storage
        $path = Storage::disk($disk)->put('uploads/products', $request->file('image'));
        $url = Storage::disk($disk)->url($path);
    } else {
        // Upload to local storage
        $path = $request->file('image')->store('uploads/products', 'local');
        $url = asset($path);
    }

    // Save to database
    $upload = new Upload;
    $upload->file_name = $path;
    $upload->file_original_name = $request->file('image')->getClientOriginalName();
    $upload->file_size = $request->file('image')->getSize();
    $upload->extension = $request->file('image')->getClientOriginalExtension();
    $upload->type = $request->file('image')->getMimeType();
    $upload->user_id = auth()->id();
    $upload->save();

    return response()->json([
        'success' => true,
        'path' => $path,
        'url' => $url
    ]);
}

Account Deletion with Storage

When deleting user accounts, clean up storage:
app/Http/Controllers/Auth/LoginController.php
public function account_deletion(Request $request)
{
    $auth_user = auth()->user();

    // Delete user images from storage
    $uploads = $auth_user->uploads;
    if ($uploads) {
        foreach ($uploads as $upload) {
            if (env('FILESYSTEM_DRIVER') == 's3') {
                Storage::disk('s3')->delete($upload->file_name);
                
                // Also delete local cache if exists
                if (file_exists(public_path() . '/' . $upload->file_name)) {
                    unlink(public_path() . '/' . $upload->file_name);
                }
                
                $upload->delete();
            } else {
                // Delete from local storage
                unlink(public_path() . '/' . $upload->file_name);
                $upload->delete();
            }
        }
    }

    // Delete user account
    User::destroy(auth()->user()->id);
    auth()->guard()->logout();
    $request->session()->invalidate();

    flash(translate("Your account deletion successfully done."))->success();
    return redirect()->route('home');
}

File Types and Limits

Image Upload Validation

$request->validate([
    'image' => 'required|image|mimes:jpeg,png,jpg,gif,svg|max:2048',
]);

Document Upload Validation

$request->validate([
    'document' => 'required|mimes:pdf,doc,docx|max:5120', // 5MB
]);

Video Upload Validation

$request->validate([
    'video' => 'required|mimes:mp4,mov,avi|max:51200', // 50MB
]);

CDN Integration

CloudFront with S3

For better performance, use CloudFront CDN:
1

Create CloudFront Distribution

In AWS Console, go to CloudFront → Create Distribution
2

Set Origin

Origin Domain: your-bucket-name.s3.amazonaws.com
3

Configure Settings

  • Viewer Protocol Policy: Redirect HTTP to HTTPS
  • Allowed HTTP Methods: GET, HEAD, OPTIONS, PUT, POST, PATCH, DELETE
  • Cache Policy: CachingOptimized
4

Update AWS_URL

.env
AWS_URL=https://your-distribution-id.cloudfront.net

Image Optimization

Intervention Image

Ecom uses Intervention Image for manipulation:
composer.json
"require": {
    "intervention/image": "^2.5"
}

Resize Before Upload

use Intervention\Image\Facades\Image;

public function uploadAndResize(Request $request)
{
    $image = $request->file('image');
    
    // Resize image
    $resized = Image::make($image)
        ->resize(1200, null, function ($constraint) {
            $constraint->aspectRatio();
            $constraint->upsize();
        })
        ->encode('jpg', 85);
    
    // Upload to storage
    $filename = time() . '.jpg';
    Storage::disk('s3')->put('products/' . $filename, $resized->__toString());
}

Backup Strategy

Automated Backups

For local storage, implement backups:
# Add to cron
0 2 * * * php /path/to/artisan backup:run
For cloud storage, enable versioning:
  • S3: Enable versioning in bucket settings
  • Backblaze: B2 automatically keeps file versions

Security Best Practices

Storage Security Guidelines
  • Never commit credentials to version control
  • Use IAM policies with minimum required permissions
  • Enable bucket versioning for recovery
  • Implement file type validation
  • Scan uploads for malware
  • Set appropriate file size limits
  • Use signed URLs for private files
  • Enable bucket logging for audit trail
  • Regularly review access permissions
  • Use HTTPS for all file transfers

Private Files

Generating Signed URLs

For private downloads:
// S3 temporary URL (expires in 1 hour)
$url = Storage::disk('s3')->temporaryUrl(
    'private/document.pdf', 
    now()->addHours(1)
);

// Or use presigned URLs
$s3 = Storage::disk('s3')->getDriver()->getAdapter()->getClient();
$command = $s3->getCommand('GetObject', [
    'Bucket' => env('AWS_BUCKET'),
    'Key' => 'private/document.pdf'
]);

$request = $s3->createPresignedRequest($command, '+20 minutes');
$url = (string) $request->getUri();

Cost Optimization

S3 Storage Classes

  • Standard: Frequent access
  • Intelligent-Tiering: Automatic cost optimization
  • Glacier: Archive (slower retrieval)

Lifecycle Policies

Automate file archival:
{
  "Rules": [
    {
      "Status": "Enabled",
      "Transitions": [
        {
          "Days": 30,
          "StorageClass": "STANDARD_IA"
        },
        {
          "Days": 90,
          "StorageClass": "GLACIER"
        }
      ]
    }
  ]
}

Troubleshooting

Cause: Incorrect IAM permissions or credentialsSolution:
  • Verify AWS credentials in .env
  • Check IAM user has S3 permissions
  • Ensure bucket policy allows access
  • Verify region is correct
Cause: Bucket is private or CORS not configuredSolution:
  • Make bucket public or use signed URLs
  • Configure CORS policy
  • Check CloudFront settings if using CDN
Cause: Large file or slow connectionSolution:
  • Increase PHP max_execution_time
  • Increase upload_max_filesize and post_max_size
  • Use chunked uploads for large files
  • Optimize file before upload
Cause: Many large files or frequent accessSolution:
  • Implement image optimization
  • Use lifecycle policies for archival
  • Delete unused files
  • Consider switching to Backblaze B2
  • Enable compression

Migration Between Storage Providers

Local to S3 Migration

use Illuminate\Support\Facades\Storage;

public function migrateToS3()
{
    $uploads = Upload::all();
    
    foreach ($uploads as $upload) {
        $localPath = public_path($upload->file_name);
        
        if (file_exists($localPath)) {
            // Upload to S3
            $s3Path = Storage::disk('s3')->putFileAs(
                dirname($upload->file_name),
                new \Illuminate\Http\File($localPath),
                basename($upload->file_name)
            );
            
            // Update database
            $upload->file_name = $s3Path;
            $upload->save();
            
            // Optionally delete local file
            // unlink($localPath);
        }
    }
}

Performance Optimization

  • Use CDN for static assets
  • Enable browser caching
  • Compress images before upload
  • Use lazy loading for images
  • Implement progressive image loading
  • Cache file URLs

Media Management

Managing uploaded files

Product Images

Product image handling

Build docs developers (and LLMs) love