Filament Plugins

Purchase

Advanced file upload

The most important component in the package is the AdvancedFileUpload component. This component is a form component that you can use to replace any FileUpload, with minimal changes to your code. Every time a file is uploaded, it is first uploaded (in chunks or using S3 Multipart) to the temporary file upload disk. Then, when you save the form, the files are moved to the disk you specify.

php
use RalphJSmit\Filament\Upload\Filament\Forms\Components\AdvancedFileUpload;

AdvancedFileUpload::make('profile_path')
    ->temporaryFileUploadDisk('s3') // Default to `config('livewire.temporary_file_upload.disk')` or `config('filesystems.default')`...
    ->disk('s3')  // Default to `config('filament.default_filesystem_disk')`

As the files get moved between disks, it is therefore optimal if you use the same disk for temporary file uploads as the permanent disk, but it is not a requirement. You can also use scoped disks, which the package will handle automatically.

Make sure to have an array or collection cast on your Eloquent model to contain an array of file paths.

Setting a directory

To place and read uploaded files from a certain directory on your disk, use the ->directory() method:

php
AdvancedFileUpload::make('banner_images')
    ->disk('s3')
    ->directory('banners')
    ->multiple()

When you set a directory, the directory is not prefixed to the dehydrated state. So if you upload a file in the above example, the dehydrated state will be an array of purely the filenames without starting with banners/, as this is done automatically for convenience and flexibility. However, the native Filament FileUpload does prefix the directory to the dehydrated state. If you would like to have the same behavior, you can pass shouldDehydrateDirectory: true as second parameter to the ->directory() method.

If you upload an image, it will get placed automatically in the banners directory. Only the filename inside the banners directory will be returned, so if you upload a file banners/banner.jpg, only banner.jpg will be returned.

Accepted file types & preserving filenames (security)

You can specify which file types are allowed to be uploaded using the ->acceptedFileTypes() method. This method accepts an array of mimetypes or file extensions:

php
AdvancedFileUpload::make('brochure_path')
    ->acceptedFileTypes(['application/pdf'])

To accept images, use ->image(), which is an alias for the image/* mimetype group.

By default, the original filenames of the uploaded files are not preserved, but assigned a random name on the server with the neutral extension .upload. When the file is fully uploaded, the package will first retrieve the mimetype of the file content, and validate it against the accepted file types. Only if the mimetype (based on the actual file-content) is valid and accepted, the file will be renamed to the extension that belongs to this mimetype. This way, the package never trusts the file-extension provided the user (which can be easily manipulated), and is only looked at the contents of the file.

If you want to preserve the original filenames, you can use the ->preserveFilenames(). This method is safe to use for S3-based disks, as there no risk of malicious code execution is present. However, you should never use this for the public disk (or even local).

php
AdvancedFileUpload::make('brochure_path')
    ->acceptedFileTypes(['application/pdf'])
    ->preserveFilenames()

Another aspect to be aware of is that if the filename can be determined by the user, then this can lead to overwrites of existing files if not properly validated.

Storing filenames in independent column

As using ->preserveFilenames() is not optimal, you can also choose to let uploads be assigned a random generated name, but store the original filenames in a separate column. This way you can still show users the original filename, whilst ensuring security and file uniqueness on your actual disk. To enable this, use the ->storeFileNamesIn() method:

php
AdvancedFileUpload::make('brochure_path')
    ->acceptedFileTypes(['application/pdf'])
    ->storeFileNamesIn('brochure_filename')

It also works for multiple files:

php
AdvancedFileUpload::make('brochure_paths')
    ->acceptedFileTypes(['application/pdf'])
    ->storeFileNamesIn('brochure_filenames')
    ->multiple()

This method can be enabled safely even if you already have files, as the AdvancedFileUpload will look at the second column to see if any alias is defined, and if not, it will automatically fall back to the actual filename as on disk. Make sure that for single upload the filename column is a string, whereas for multiple uploads the filename column is a JSON column with an array or collection cast.

Fetching file information

When rendering existing files in the upload component, you have the choice to prevent the package from getting the file type, size and whether it exists by using the ->fetchFileInformation(false) method:

php
AdvancedFileUpload::make('banner_images')
    ->fetchFileInformation(false)

One downside of this is that the package cannot render an appropriate preview, and neither can it remove non-existing file paths on mount.

The file size and mimetype are getting cached, so for performance you don't necessarily have to disable this.

Spatie MediaLibrary support

The package supports an automatic integration with Spatie MediaLibrary. To enable the automatic loading of media files, use the ->spatieMediaLibrary() method:

php
AdvancedFileUpload::make('media')
    ->spatieMediaLibrary()
    ->multiple() // In case of single-support, only the first image will be loaded.

When enabled, this will take the current $record and load the media files from the MediaLibrary into the upload. Users can then view, edit, download, replace and delete these files. When a user saves the form, the media collection is updated.

To use a different collection, provide a collection: $collection parameter:

php
AdvancedFileUpload::make('media')
    ->spatieMediaLibrary(collection: 'banners')
    ->multiple()

Providing custom properties

You may pass in custom properties when uploading files using the ->customProperties() method:

php
AdvancedFileUpload::make('banners')
    ->spatieMediaLibrary(collection: 'banners')
    ->multiple()
    ->customProperties(['zip_filename_prefix' => 'folder/subfolder/'])

Providing custom headers

You may pass in custom headers when uploading files using the ->customHeaders() method:

php
AdvancedFileUpload::make('banners')
    ->spatieMediaLibrary(collection: 'banners')
    ->multiple()
    ->customHeaders(['CacheControl' => 'max-age=86400'])

Filtering media

It's possible to target a file upload component to only handle a certain subset of media in a collection. To do that, you can filter the media collection using the filterMediaUsing() method. This method accepts a function that receives the $media collection and manipulates it. You can use any collection method to filter it.

For example, you could scope the field to only handle media that has certain custom properties:

php
AdvancedFileUpload::make('banners')
    ->spatieMediaLibrary(collection: 'banners')
    ->customProperties(fn (Get $get): array => [
        'gallery_id' => $get('gallery_id'),
    ])
    ->filterMediaUsing(function (Collection $media, Get $get): Collection {
        return $media->where('custom_properties.gallery_id', $get('gallery_id'));
    })

Multiple uploads

To allow multiple uploads, use the ->multiple() method:

php
AdvancedFileUpload::make('banner_images')
    ->disk('s3')
    ->multiple()

Minimum and maximum file count

You can specify a minimum and maximum number of files that can be uploaded using the ->minFiles() and ->maxFiles() methods:

php
AdvancedFileUpload::make('banner_images')
    ->multiple()
    ->minFiles(1) 
    ->maxFiles(5)

Minimum and maximum file size

You can specify a minimum and maximum file size that can be uploaded using the ->minFileSize() and ->maxFileSize() methods. The file size is specified in bytes:

php
AdvancedFileUpload::make('banner_images')
    ->multiple()
    ->minFileSize(1024) // 1 KB
    ->maxFileSize(1024 * 1024) // 10 MB

This rule is only applied to newly uploaded files, as applying this to already existing files on the server would not be a great UX in case you ever must change this validation, and because it would make every form save very slow.

Reordering files

If you have multiple file-upload, you can allow users to reorder files using the ->reorderable() method. This will modify the order in which the file paths are dehydrated:

php
AdvancedFileUpload::make('banner_images')
    ->multiple()
    ->reorderable()

Reordering is also supported when using the Spatie MediaLibrary-integration.

Custom upload logic

By default, when a user uploads a file, the upload is first stored on the ->temporaryFileUploadDisk(), within the ->temporaryFileUploadDirectory() (usually livewire-tmp), and then within a nested directory. When a user submits the form, the file is moved (if the temporary disk is the same as the permanent disk) or copied to its permanent location.

If you want, you can provide a custom logic to replace this process, so that you can modify the file or determine the permanent file location yourself. To do use, use the ->saveUploadedFileUsing() method:

php
AdvancedFileUpload::make('banner_images')
    ->multiple()
    ->saveUploadedFileUsing(function (AdvancedFileUpload $component, string $temporaryFileUploadPath, string $temporaryFileUploadFilename) {
        // Temporary disk...
        $temporaryFileUploadDisk = $component->getTemporaryFileUploadDisk();
        $temporaryFileUploadDirectory = $component->getTemporaryFileUploadDirectory();

        // Permanent disk...
        $disk = $component->getDisk();
        $directory = $component->getDirectory();
        $diskData = $component->getDiskData();
        
        // Copy/move the file to permanent location...
        $disk->put(
            path: $diskData->generatePath($directory, $temporaryFileUploadFilename),
            contents: $temporaryFileUploadDisk->readStream($temporaryFileUploadPath),
        );
        
        // Always return the _filename_ that the file was moved to (excluding the `$directory` prefix).
        return $temporaryFileUploadFilename;
    });

This hook can be called multiple times in the same request for different files if a user uploads multiple new files at once.

Uploading message

When a file is uploading, the "submit" button of the form will be temporarily disabled with the following text: 'Uploading file...'. To modify this message, use the ->uploadingMessage() method:

php
AdvancedFileUpload::make('banner_images')
    ->multiple()
    ->uploadingMessage('Uploading banner...')

It is recommended to keep the message short.

Layout options

The AdvancedFileUpload component is a column-based component, meaning that it exists of two child-groups: the dropzone and the files-section. For each, you can specify how many columns it should take up. For example, for a vertical layout, you can configure it like this:

php
AdvancedFileUpload::make('profile_path')
    ->columns(5) // 5 columns total
    ->dropzoneColumnSpan(2) // Dropzone section should span 2 columns
    ->filesColumnSpan(3) // Files section should span 3 columns
    ->dropzoneAfterFiles() // Dropzone should come after (or before: default) the files

Dropzone area

The default dropzone heading and description can be modified using the following methods:

php
AdvancedFileUpload::make('profile_path')
    ->dropzoneHeading('Drag & drop your profile image here')
    ->dropzoneDescription('or, click to select an image')

Above the heading and description three icons are displayed. You can modify these using the ->dropzonePreview{}Icon() methods, to better match the type of files you are expecting:

php
use Filament\Support\Icons\Heroicon;

AdvancedFileUpload::make('profile_path')
    ->dropzonePreviewLeftIcon(Heroicon::OutlinedDocument)
    ->dropzonePreviewCenterIcon(Heroicon::OutlinedPhoto)
    ->dropzonePreviewRightIcon(Heroicon::OutlinedPaperClip)

Instead of icons, you can also provide three images as previews. To do so, use the ->dropzonePreviewImages() method:

php
AdvancedFileUpload::make('profile_path')
    ->dropzonePreviewImages(
        leftImageUrl: asset('preview-a-small.png'),
        centerImageUrl: asset('preview-b-small.png'),
        rightImageUrl: asset('preview-c-small.png')
    )

Files

The AdvancedFileUpload component provides several actions that can be performed on files. Each action can be customized or disabled using the following methods:

Add file action

The add file action allows users to select files from their device to upload. You can customize this action using the ->addAction() method:

php
use Filament\Support\Icons\Heroicon;

AdvancedFileUpload::make('banner_images')
    ->addAction(function (Forms\Components\Actions\Action $action) {
        return $action
            ->label('Upload banners')
            ->icon(Heroicon::OutlinedPhoto);
    })

Delete file action

The delete file action allows users to remove individual files. You can control whether files can be deleted using the ->deletable() method, and customize the action using the ->deleteAction() method:

php
use Filament\Support\Icons\Heroicon;

AdvancedFileUpload::make('banner_images')
    ->deleteAction(function (Forms\Components\Actions\Action $action) {
        return $action
            ->label('Remove banner')
            ->icon(Heroicon::OutlinedTrash);
    })

To disable the action, use:

php
AdvancedFileUpload::make('banner_images')
    ->deletable(false)

Delete all files action

The delete all files action allows users to remove all files at once. You can control whether all files can be deleted using the ->deleteAll() method, and customize the action using the ->deleteAllAction() method:

php
use Filament\Support\Icons\Heroicon;

AdvancedFileUpload::make('banner_images')
    ->deleteAllAction(function (Forms\Components\Actions\Action $action) {
        return $action
            ->label('Clear all banners')
            ->icon(Heroicon::OutlinedTrash);
    })

To disable the action, use:

php
AdvancedFileUpload::make('banner_images')
    ->deleteAll(false)

Download file action

The download file action allows users to download individual files. You can control whether files can be downloaded using the ->downloadable() method, and customize the action using the ->downloadAction() method:

php
use Filament\Support\Icons\Heroicon;

AdvancedFileUpload::make('banner_images')
    ->downloadAction(function (Forms\Components\Actions\Action $action) {
        return $action
            ->label('Download banner')
            ->icon(Heroicon::OutlinedArrowDownTray);
    })

To disable the action, use:

php
AdvancedFileUpload::make('banner_images')
    ->downloadable(false)

Edit file action

The edit file action allows users to edit image files using the built-in image editor. You can control whether files can be edited using the ->editable() method, and customize the action using the ->editAction() method:

php
use Filament\Support\Icons\Heroicon;

AdvancedFileUpload::make('banner_images')
    ->editAction(function (Forms\Components\Actions\Action $action) {
        return $action
            ->label('Edit banner')
            ->icon(Heroicon::OutlinedPencilSquare);
    })

To disable the action, use:

php
AdvancedFileUpload::make('banner_images')
    ->editable(false)

Preview file action

The preview file action allows users to view files in a modal. You can control whether files can be previewed using the ->previewable() method, and customize the action using the ->previewAction() method:

php
use Filament\Support\Icons\Heroicon;

AdvancedFileUpload::make('banner_images')
    ->previewAction(function (Forms\Components\Actions\Action $action) {
        return $action
            ->label('View banner')
            ->icon(Heroicon::OutlinedEye);
    })

To disable the action, use:

php
AdvancedFileUpload::make('banner_images')
    ->previewable(false)

Replace file action

The replace file action allows users to replace an existing file with a new one. You can control whether files can be replaced using the ->replaceable() method, and customize the action using the ->replaceAction() method:

php
use Filament\Support\Icons\Heroicon;

AdvancedFileUpload::make('banner_images')
    ->replaceAction(function (Forms\Components\Actions\Action $action) {
        return $action
            ->label('Replace banner')
            ->icon(Heroicon::OutlinedArrowPathRoundedSquare);
    })

To disable the action, use:

php
AdvancedFileUpload::make('banner_images')
    ->replaceable(false)

Custom file actions

You can add custom actions to each file by using the ->extraItemActions() method. Actions are inserted just before the "delete"-action, as that is usually the most logical order. Inside the ->action() method, you can retrieve the disk name and the path to the file that the action was triggered on. This allows you to perform any action that has a relation to an individual file.

php
use Filament\Support\Icons\Heroicon;

AdvancedFileUpload::make('banner_images')
    ->extraItemActions([
        fn (AdvancedFileUpload $component) => Forms\Components\Actions\Action::make('optimize')
            ->icon(Heroicon::Sparkles)
            ->iconButton()
            ->color('primary')
            ->action(function (array $arguments) use ($component) {
                [$diskName, $path] = $component->getDiskNameAndPath($arguments['id']);

                // Retrieve file...
                $contents = Storage::disk($diskName)->get($path);

                // Modify file based on `$diskName` and `$path`. Note: do *not* move
                // the file as that will cause the component to end up invalid.

                // The `$path` is the filename + the provided `->directory()` if specified.
            }),
    ])

Note: when using custom actions, you should always use the $component->getDiskNameAndPath() method to retrieve the disk name and path of the file that the action was triggered on. This ensures that you are working with the correct file. Furthermore, do not modify the file name or its location, as then the component ends up with an invalid state.

© FilamentPlugins.com ✦ 2022 – 2026
PrivacyTerms & Conditions
All rights reserved.