Feature: #103511 - Introduce Extbase file upload and deletion handling
See forge#103511
Description
TYPO3 now provides an API for file upload- and deletion-handling in Extbase extensions, which allows extension developers to implement file uploads more easily into Extbase Domain Models.
The scope of this API is to cover some of the most common use cases and to keep the internal file upload and deletion process in Extbase as simple as possible.
The API supports mapping and handling of file uploads and deletions for the following scenarios:
- Property of type
File
in a domain modelReference - Property of type
\TYPO3\
in a domain modelCMS\ Extbase\ Persistence\ Object Storage<\ TYPO3\ CMS\ Extbase\ Domain\ Model\ File Reference>
File uploads can be validated by the following rules:
- minimum and maximum file count
- minimum and maximum file size
- allowed MIME types
- image dimensions (for image uploads)
Additionally, it is ensured, that the filename given by the client is valid, meaning that no invalid characters (null-bytes) are added and that the file does not contain an invalid file extension. The API has support for custom validators, which can be created on demand.
To avoid complexity and maintain data integrity, a file upload is only processed if the validation of all properties of a domain model is successful. In this first implementation, file uploads are not persisted/cached temporarily, so this means in any case of a validation failure ("normal" validators and file upload validation) a file upload must be performed again by users.
Possible future enhancements of this functionality could enhance the existing
#
attribute/annotation with configuration like a temporary storage
location, or specifying additional custom validators (which can be done via the PHP-API as
described below)
Nesting of domain models
File upload handling for nested domain models (e.g. modelA.modelB.fileReference) is not supported.
File upload configuration with the FileUpload
attribute
File upload for a property of a domain model can be configured using the
newly introduced
\TYPO3\
attribute.
Example:
#[FileUpload([
'validation' => [
'required' => true,
'maxFiles' => 1,
'fileSize' => ['minimum' => '0K', 'maximum' => '2M'],
'mimeType' => ['allowedMimeTypes' => ['image/jpeg', 'image/png']],
],
'uploadFolder' => '1:/user_upload/files/',
])]
protected ?FileReference $file = null;
All configuration settings of the
\TYPO3\
object can
be defined using the
File
attribute. It is however not possible
to add custom validators using the
File
attribute, which you
can achieve with a manual configuration as shown below.
The currently available configuration array keys are:
validation
(array
with keysrequired
,max
,Files min
,Files file
,Size allowed
,Mime Types mime
,Type image
, see File upload validation)Dimensions upload
(Folder string
, destination folder)duplication
(Behavior object
, behaviour when file exists)add
(Random Suffix bool
, suffixing files)create
(Upload Folder If Not Exist bool
, whether to create missing directories)
It is also possible to use the
File
annotation to configure
file upload properties, but it is recommended to use the
File
attribute due to better readability.
Manual file upload configuration
A file upload configuration can also be created manually and should be
done in the
initialize*Action
.
Example:
public function initializeCreateAction(): void
{
$mimeTypeValidator = GeneralUtility::makeInstance(MimeTypeValidator::class);
$mimeTypeValidator->setOptions(['allowedMimeTypes' => ['image/jpeg']]);
$fileHandlingServiceConfiguration = $this->arguments->getArgument('myArgument')->getFileHandlingServiceConfiguration();
$fileHandlingServiceConfiguration->addFileUploadConfiguration(
(new FileUploadConfiguration('myPropertyName'))
->setRequired()
->addValidator($mimeTypeValidator)
->setMaxFiles(1)
->setUploadFolder('1:/user_upload/files/')
);
$this->arguments->getArgument('myArgument')->getPropertyMappingConfiguration()->skipProperties('myPropertyName');
}
Configuration options for file uploads
The configuration for a file upload is defined in a
File
object.
This object contains the following configuration options.
Hint
The appropriate setter methods or configuration keys can best be inspected inside that class definition.
Property name:
Defines the name of the property of a domain model to which the file upload
configuration applies. The value is automatically retrieved when using
the
File
attribute. If the
File
object
is created manually, it must be set using the
$property
constructor argument.
Validation:
File upload validation is defined in an array of validators in the
File
object. The validator
\TYPO3\
,
which ensures that no executable PHP files can
be uploaded, is added by default if the file upload configuration object
is created using the
File
attribute.
In addition, Extbase includes the following validators to validate an
Uploaded
object:
\TYPO3\
CMS\ Extbase\ Validation\ Validator\ File Size Validator \TYPO3\
CMS\ Extbase\ Validation\ Validator\ Mime Type Validator \TYPO3\
CMS\ Extbase\ Validation\ Validator\ Image Dimensions Validator
Those validators can either be configured with the
File
attribute or added
manually to the configuration object
with the
add
method.
Required:
Defines whether a file must be uploaded. If it is set to true
, the
min
configuration is set to 1
.
Minimum files:
Defines the minimum amount of files to be uploaded.
Maximum files:
Defines the maximum amount of files to be uploaded.
Upload folder:
Defines the upload path for the file upload. This configuration expects a
storage identifier (e.g.
1:/
). If the given target
folder in the storage does not exist, it is created automatically.
Upload folder creation, when missing:
The default creation of a missing storage folder can be disabled via the
configuration attribute
create
(
bool
, default
true
).
Add random suffix:
When enabled, the filename of an uploaded and persisted file will contain a
random 16 char suffix. As an example, an uploaded file named
job-
will be persisted as
job-
in the upload folder.
The default value for this configuration is
true
and it is recommended
to keep this configuration active.
This configuration only has an effect when uploaded files are persisted.
Duplication behavior:
Defines the FAL behavior, when a file with the same name exists in the target
folder. Possible values are
Duplication
(default),
Duplication
and
Duplication
.
Modifying existing configuration
File upload configuration defined by the
File
attribute can be
changed in the
initialize*Action
.
Example:
public function initializeCreateAction(): void
{
$validator = GeneralUtility::makeInstance(MyCustomValidator::class);
$argument = $this->arguments->getArgument('myArgument');
$configuration = $argument->getFileHandlingServiceConfiguration()->getFileUploadConfigurationForProperty('file');
$configuration?->setMinFiles(2);
$configuration?->addValidator($validator);
$configuration?->setUploadFolder('1:/user_upload/custom_folder');
}
The example shows how to modify the file upload configuration for the argument
item
and the property
file
. The minimum amount of files to be
uploaded is set to
2
and a custom validator is added.
To remove all defined validators except the
Deny
, use
the
reset
method.
Using TypoScript configuration for file uploads configuration
When a file upload configuration for a property has been added using the
File
attribute, it may be
required make the upload folder or
other configuration options configurable with TypoScript.
Extension authors should use the
initialize*Action
to apply settings
from TypoScript to a file upload configuration.
Example:
public function initializeCreateAction(): void
{
$argument = $this->arguments->getArgument('myArgument');
$configuration = $argument->getFileHandlingServiceConfiguration()->getConfigurationForProperty('file');
$configuration?->setUploadFolder($this->settings['uploadFolder'] ?? '1:/fallback_folder');
}
File upload validation
Each uploaded file can be validated against a configurable set of validators.
The
validation
section of the
File
attribute allows to
configure commonly used validators using a configuration shorthand.
The following validation rules can be configured in the
validation
section of the
File
attribute:
required
min
Files max
Files file
(forSize \TYPO3\
)CMS\ Extbase\ Validation\ Validator\ Filesize Validator image
(forDimensions \TYPO3\
)CMS\ Extbase\ Validation\ Validator\ Image Dimensions Validator mime
(forType \TYPO3\
)CMS\ Extbase\ Validation\ Validator\ Mime Type Validator allowed
(shorthand notation for configuration optionMime Types allowed
of theMime Types Mime
)Type Validator
Example:
#[FileUpload([
'validation' => [
'required' => true,
'maxFiles' => 1,
'fileSize' => ['minimum' => '0K', 'maximum' => '2M'],
'mimeType' => ['allowedMimeTypes' => ['image/jpeg']],
'imageDimensions' => ['maxWidth' => 4096, 'maxHeight' => 4096]
],
'uploadFolder' => '1:/user_upload/extbase_single_file/',
])]
Extbase will internally use the Extbase file upload validators for
file
,
mime
and
image
validation.
Custom validators can be created according to project requirements and must
extend the Extbase
Abstract
.
The value to be validated is
always a PSR-7
Uploaded
object.
Custom validators can however not
be used in the
File
attribute
and must be configured manually.
Shorthand notation for allowedMimeTypes
Using the
mime
configuration array, all options of the Mime
can be set as sub-keys (since TYPO3 13.4.1):
#[FileUpload([
'validation' => [
'required' => true,
'mimeType' => [
'allowedMimeTypes' => ['image/jpeg'],
'ignoreFileExtensionCheck' => false,
'notAllowedMessage' => 'LLL:EXT:my_extension/Resources/Private/Language/locallang.xlf:validation.mimetype.notAllowedMessage',
'invalidExtensionMessage' => 'LLL:EXT:my_extension/Resources/Private/Language/locallang.xlf:validation.mimetype.invalidExtensionMessage',
],
],
'uploadFolder' => '1:/user_upload/files/',
])]
The shorthand notation via
'allowed
continues to
exist, in case only the mime type validation is needed. However, it is recommended
to utilize the full
'mime
configuration array.
Deletion of uploaded files and file references
The new Fluid ViewHelper Form.uploadDeleteCheckbox ViewHelper <f:form.uploadDeleteCheckbox> can be used to show a "delete file" checkbox in a form.
Example for object with
File
property:
<f:form.uploadDeleteCheckbox property="file" fileReference="{object.file}" />
Example for an object with an
TYPO3\
property, containing multiple files and allowing to delete the first one
(iteration is possible within Fluid, to do that for every object of the collection):
<f:form.uploadDeleteCheckbox property="file.0" fileReference="{object.file}" />
Extbase will then handle file deletion(s) before persisting a validated object. It will:
- validate that minimum and maximum file upload configuration for the affected
property is fulfilled (only if the property has a
File
)Upload - delete the affected
sys_
recordfile_ reference - delete the affected file
Internally, Extbase uses
File
objects to track
file deletions for properties of arguments. Files are deleted directly without
checking whether the current file is referenced by other objects.
Apart from using this ViewHelper, it is of course still possible to manipulate
File
properties with custom logic before persistence.
New PSR-14 events
The following new PSR-14 event has been added to allow customization of file upload related tasks:
ModifyUploadedFileTargetFilenameEvent
The
Modify
allows event listeners to
alter a filename of an uploaded file before it is persisted.
Event listeners can use the method get
to retrieve the filename
used for persistence of a configured uploaded file. The filename can then be
adjusted via set
. The relevant configuration can be retrieved
via get
.
Impact
Extension developers can use the new feature to implement file uploads and file deletions in Extbase extensions easily with commonly known Extbase property attributes/annotations.