iperamuna/filament-chunk-upload
Install the package and its FilePond dependency, then publish the FilePond config and migrations — the chunk upload component will not work without the underlying rahulhaque/laravel-filepond temp-upload table:
composer require iperamuna/filament-chunk-upload
php artisan vendor:publish --provider="RahulHaque\Filepond\FilepondServiceProvider"
php artisan migrate
The first place to look is the FilePond config file (config/filepond.php). It defines the temporary upload disk/directory and, critically, the server.url value that Filament Chunk Upload reads when wiring up the frontend component. If your app is served from a subdirectory, behind a proxy, or on a non-default domain, set this explicitly — a mismatch here is the single most common cause of "uploads start and immediately fail."
First use case: a large media field on a Filament resource. Swap FileUpload::make() for ChunkedFileUpload::make():
use Iperamuna\FilamentChunkUpload\ChunkedFileUpload;
ChunkedFileUpload::make('video_file')
->label('Upload Video')
->chunkSize(10485760) // 10MB
->directory('videos')
->visibility('private')
->required();
Start with a single-file field on a private disk, upload something larger than upload_max_filesize to confirm the chunk pipeline works end-to-end, then expand to multiple fields. If your default disk is S3, test both the temp-disk and final-disk paths early — the dehydrate step moves files between them and is where misconfigured disks surface.
Chunk size tuning. chunkSize() is in bytes and defaults to 10MB. Lower it (2–5MB) for flaky mobile connections or restrictive proxies; raise it (25–50MB) for stable server-to-server or desktop uploads. Keep chunks comfortably below post_max_size and any reverse-proxy body limit — the point of chunking is that each request stays small, so a chunk size above the server limit defeats the package entirely.
Disk and visibility strategy. For anything sensitive (KYC documents, contracts, raw video), use ->visibility('private') plus a non-public disk. The package preserves original filenames on dehydrate, which is convenient for user-facing downloads but means you should not rely on the stored name for uniqueness — verify collision handling on your disk, or pre-process the name in saveUploadedFileUsing().
Serving private files. Use the documented streamDownload pattern in a dedicated controller with authorization checks rather than exposing the disk:
return response()->streamDownload(
fn () => readfile(Storage::disk('private')->path($record->video_file)),
basename($record->video_file)
);
Wrap it in a policy or signed-URL route — the package handles storage, not access control.
Validation. The component deliberately bypasses Filament's array-based validation for single-string FilePond IDs, so treat validation as your responsibility. Apply ->acceptedFileTypes(), ->maxSize(), and ->required() explicitly, and consider a ->rule() for MIME sniffing. Because the file lands as a temp upload first, size validation against the dehydrated file is what actually matters.
Multiple files and repeaters. For ->multiple(), confirm the temp-ID-to-stored-file mapping survives dehydrate in your Filament version — test with three small files before shipping a repeater or relation-manager implementation. Multi-file fields in repeaters are the least-exercised path in a v1.0.0 package.
Integration tips. Keep the FilePond temp directory on the same filesystem as the final disk where possible (avoids a copy of every large file). Pair the field with a queued job for downstream processing (transcoding, thumbnails) triggered on afterCreate/afterSave, not in the form's save() — the dehydrate move is synchronous and you don't want to stack work on top of it.
upload_max_filesize/post_max_size, and Nginx's client_max_body_size applies per request. Set these to comfortably exceed your chunk size, not your total file size. Also check max_input_time and proxy idle timeouts for slow chunk sequences.created_at) — the package ships no garbage collection in v1.0.0.filepond.server.url misconfiguration is the top failure mode in local dev and behind proxies. If chunks upload but the field never resolves, log the network tab first: 404/419 on the patch endpoint usually means the URL or CSRF/session config, not the package.video.mp4 can collide depending on disk behaviour. Test and, if needed, override the save callback to inject a ULID prefix.BaseFileUpload (saveUploadedFileUsing, getUploadedFileNameForStorageUsing) — override those rather than forking the package for naming or post-processing needs.rahulhaque/laravel-filepond and tight coupling to Filament's BaseFileUpload internals. Pin versions in composer.json and re-test chunk uploads on every Filament minor upgrade — that's the most likely source of breakage.How can I help you explore Laravel packages today?