As of July 2025, Typo3 is not able to proceed input files where multiple files are selectable.
There is one advertised plugin:
https://github.com/studiomitte/form_multiple_uploads/
It has looked very good in the beginning. But this seems at least not fully compatible with Typo3 V12 and has one big disadvance:
The submitted files can not be attached to emails.
Other solutions like:
- https://stackoverflow.com/questions/54175129/how-to-multiupload-in-form-of-typo3-v9-5
- studiomitte/form_multiple_uploads#1
Seems also outdated or important parts are missing
The biggest problem is to handle the request with multiple files and to process the FALs in the correct way.
So, as i'm not able to dig more deep into the Typo3 way there is my workaround:
- Add 10 default input file fields
- Modify the first input field slicly to accept multiple files
- Distribute the uploaded files to the other input fields (Not possible by javascript, because Safari blocks this)
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/form']['afterSubmit'][1700000000]
= \YourNamespace\YourPlugin\Form\Hook\DistributeUploadsHook::class;
<?php
declare(strict_types=1);
namespace YourNamespace\YourPlugin\Form\Hook;
use TYPO3\CMS\Form\Domain\Runtime\FormRuntime;
use TYPO3\CMS\Form\Domain\Model\Renderable\RenderableInterface;
/**
* Fabian Bett / Bett Ingenieure GmbH
* July 2025
* For Typo3 v12.4.16
*
* Workaround to fix the issue, that there is no MultiUpload field:
*
* 1. Provide one input file field
* - with identifier "attachmentsMulti"
* - add "multiple: multiple" to the "fluidAdditionalAttributes" section of the yaml to this field
* - Use javascript to append "[]" to the input name
*
* 2. Provide more input file fields with identifiers in the sequence "attachmentsMultiTarget1", "attachmentsMultiTarget2", etc..
* - Hide them using css
* - Should not be required to fill
*
* 3. (Optional) Add a "change" javascript listener to validate the maximum amount of allowed files
*
* This script will distribute the uploaded files to all file fields
*/
class DistributeUploadsHook {
private const ATTACHMENTS_MULTI_ID = 'attachmentsMulti';
private static ?array $uploadedFiles = null;
public function afterSubmit(FormRuntime $formRuntime, RenderableInterface $renderable, $elementValue, array $requestArguments = []) {
$id = $renderable->getIdentifier();
$formId = $formRuntime->getIdentifier();
if ($id === self::ATTACHMENTS_MULTI_ID) {
// Read the received files and save them to distribute them to the over inputs
self::$uploadedFiles = $formRuntime->getRequest()->getUploadedFiles();
return self::$uploadedFiles[$formId][$id][0] ?? null;
}
if (str_starts_with($id, $prefix = 'attachmentsMultiTarget')) {
$targetNumber = substr($id, strlen($prefix));
return self::$uploadedFiles[$formRuntime->getIdentifier()][self::ATTACHMENTS_MULTI_ID][intval($targetNumber)] ?? null;
}
return $elementValue;
}
}
-
properties:
saveToFileMount: '1:/user_upload/'
allowedMimeTypes:
- application/pdf
elementDescription: 'PDF Nr. 1'
fluidAdditionalAttributes:
multiple: multiple
required: required
maxFileSize: 31457280
type: FileUpload
identifier: attachmentsMulti
label: PDF(s)
validators:
-
identifier: NotEmpty
-
properties:
saveToFileMount: '1:/user_upload/'
allowedMimeTypes:
- application/pdf
maxFileSize: 31457280
type: FileUpload
identifier: attachmentsMultiTarget1
label: PDF(s)
(Add more of attachmentsMultiTarget fields, if you like)
div[class*=" container-attachmentsMultiTarget"] {
display: none !important;
}
$('input[type="file"][multiple="multiple"]', formContainer).each(function() {
if($(this).hasClass(referenceClass = 'initFormUploadField-prepared')) {
return;
}
$(this).addClass(referenceClass);
$(this).attr('name', $(this).attr('name') + '[]');
});