Laravel Packages

Prevent Duplicate File Uploads With Laravel Storage Dedupler

Published
Prevent Duplicate File Uploads With Laravel Storage Dedupler image

Laravel Storage Dedupler, by Khimusov Maxim, uses SHA-1 hashes to avoid storing duplicate files. When users upload the same document for several posts, each post gets its own attachment link to one shared file.

Deduplication applies to files stored through Dedupler. Installing the package does not change existing calls to Laravel's storage methods or remove duplicate files already on disk.

Store Files Without Duplicates

The Dedupler facade accepts uploaded files, local paths, and raw content. This example stores the same content under two filenames:

use Maxkhim\Dedupler\Facades\Dedupler;

$content = 'Notes for the November release.';

$first = Dedupler::storeFromContent($content, 'release-notes.txt');
$second = Dedupler::storeFromContent($content, 'notes-copy.txt');

if ($first === null || $second === null) {
    throw new RuntimeException('Could not store the release notes.');
}

$first->id === $second->id; // true

The package hashes the content and looks for an existing UniqueFile record before writing a new file. A matching hash returns that record, even when the incoming filename differs. The SHA-1 hash also serves as the record's primary key.

For uploads, use Dedupler::storeFromUploadedFile($file). For a file already on the server, use Dedupler::storeFromPath($absolutePath). Storage methods can return null for an invalid source or a storage error, so check the result before accessing its properties.

If you use an upload interface such as FilePond in Laravel, pass the Laravel UploadedFile received by your application to Dedupler.

Attach Files to Eloquent Models

Add the Deduplable trait to a model that needs attachments:

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Maxkhim\Dedupler\Traits\Deduplable;

class Post extends Model
{
    use Deduplable;
}

Use a saved model, so the package has a model ID to associate with the file:

use App\Models\Post;

$request->validate([
    'attachment' => ['required', 'file'],
]);

$post = Post::findOrFail($postId);
$file = $post->storeUploadedFile($request->file('attachment'));

if ($file === null) {
    throw new RuntimeException('Could not store the attachment.');
}

$anotherPost = Post::findOrFail($anotherPostId);
$anotherPost->attachUniqueFile($file->id);

The second post references the existing file by hash without uploading another copy. The attachment table records the model type and ID so that different model classes can share a file too.

The trait also provides storeLocalFile() and storeContentFile(). When you store a file through the model, Dedupler saves its original filename and status on the attachment record. Attaching by hash sets the status but does not copy the original filename; you can supply it in the second argument to attachUniqueFile().

Detach Shared Attachments

Remove a post's attachment using its file hash:

$post->detachUniqueFile($file->id);

If another model still references the file, Dedupler keeps it. Detaching the final attachment deletes both the stored file and its UniqueFile record by default. If your application must retain documents after users remove their attachments, review this behaviour before using the detach method.

Installation and Legacy Analysis

The package declares support for PHP ^8.2 and Laravel 10, 11, 12, and 13. Install it with Composer, then run its installation command:

composer require maxkhim/laravel-storage-dedupler
php artisan dedupler:install

The installation command runs the package migrations, then checks the database tables and storage configuration. The default disk is deduplicated, configured as a private local disk at storage/app/deduplicated. Dedupler writes through Laravel's storage API. For background on remote disk configuration, see our guide to using AWS S3 for Laravel storage.

To inspect an existing local directory, run:

php artisan dedupler:analyse-legacy /absolute/path/to/legacy/storage/directory

This command scans the directory and its subdirectories for matching SHA-1 hashes. It reports duplicate counts and an estimate of potential space savings. It does not migrate or delete the files.

You can find the source and usage documentation in the Laravel Storage Dedupler GitHub repository. The package is released under the MIT licence.

Yannick Lyn Fatt photo

Staff Writer at Laravel News and Full stack web developer.

Sponsored

serpapi logo
SerpApi

The Web Search API for Your LLM and AI Applications

Visit SerpApi

The latest

View all →
Laravel Herd 2: Coding Agents, Workspaces, and Cloud Deploys image

Laravel Herd 2: Coding Agents, Workspaces, and Cloud Deploys

Read article
Strict Request Input Casting in Laravel image

Strict Request Input Casting in Laravel

Read article
Slow Job Alerts and Replica Commit IDs in Laravel 13.40 image

Slow Job Alerts and Replica Commit IDs in Laravel 13.40

Read article
Flock: A Free, Native Herd Alternative for Laravel image

Flock: A Free, Native Herd Alternative for Laravel

Read article
Live Props for Real-Time Updates in Inertia.js v3.11 image

Live Props for Real-Time Updates in Inertia.js v3.11

Read article
Cooldown Dependency Policy now in Composer 2.11 image

Cooldown Dependency Policy now in Composer 2.11

Read article