---
title: "File upload"
manual: "TYPO3 Explained"
version: "12.4"
permalink: "https://docs.typo3.org/permalink/t3coreapi:extbase-fileupload@12.4"
source: "ExtensionArchitecture/Extbase/Reference/FileUpload.rst"
rendered: "2026-10-21T15:29:37+00:00"
---

> [!WARNING]
> **Attention**
>
> TYPO3 v12 has reached end-of-life as of April 30th 2026 and
> is no longer being maintained. Use the version switcher on the top
> left of this page to select documentation for a supported version of TYPO3.
>
> Need more time before upgrading?
> You can purchase Extended Long Term Support (ELTS) for TYPO3 v12 here:
> [TYPO3 ELTS](https://typo3.com/services/extended-support-elts).

# File upload {#extbase-fileupload}

Implementing file uploads / attachments to Extbase domain models
has always been a bit of a challenge.

While it is straight-forward to access an existing file reference in a domain model,
writing new files to the [FAL (File Access Layer)](https://docs.typo3.org/permalink/t3coreapi:using-fal@12.4)
takes more effort.

## Accessing a file reference in an Extbase domain model {#extbase-fileupload-accessing}

You need two components for the structural information: the Domain
Model definition and the TCA entry.

The domain model definition:

**EXT:my_extension/Classes/Domain/Model/Blog.php**

```php
<?php

declare(strict_types=1);

namespace MyVendor\MyExtension\Domain\Model;

use TYPO3\CMS\Extbase\Domain\Model\FileReference;
use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;
use TYPO3\CMS\Extbase\Persistence\ObjectStorage;

class Blog extends AbstractEntity
{
    // A single file
    protected ?FileReference $singleFile = null;

    /**
     * A collection of files.
     * @var ObjectStorage<FileReference>
     */
    protected ObjectStorage $multipleFiles;

    // When using ObjectStorages, it is vital to initialize these.
    public function __construct()
    {
        $this->multipleFiles = new ObjectStorage();
    }

    /**
     * Called again with initialize object, as fetching an entity from the DB does not use the constructor
     */
    public function initializeObject(): void
    {
        $this->multipleFiles = $this->multipleFiles ?? new ObjectStorage();
    }

    // Typical getters
    public function getSingleFile(): ?FileReference
    {
        return $this->singleFile;
    }

    /**
     * @return ObjectStorage|FileReference[]
     */
    public function getMultipleFiles(): ObjectStorage
    {
        return $this->multipleFiles;
    }

    // For later examples, the setters:
    public function setSingleFile(?FileReference $singleFile): void
    {
        $this->singleFile = $singleFile;
    }

    public function setMultipleFiles(ObjectStorage $files): void
    {
        $this->multipleFiles = $files;
    }
}

```

and the TCA definition:

**EXT:my_extension/Configuration/TCA/tx_myextension_domain_model_blog.php**

```php
<?php

return [
    'ctrl' => [
        // .. usual TCA fields
    ],
    'columns' => [
        // ... usual TCA columns
        'single_file' => [
            'exclude' => true,
            'label' => 'Single file',
            'config' => [
                'type' => 'file',
                'maxitems' => 1,
                'allowed' => 'common-image-types',
            ],
        ],
        'multiple_files' => [
            'exclude' => true,
            'label' => 'Multiple files',
            'config' => [
                'type' => 'file',
                'allowed' => 'common-image-types',
            ],
        ],
    ],
];

```

Once this is set up, you can create/edit records through the TYPO3
backend (for example via **Web > List**), attach a single or
multiple files in it. Then using a normal
controller and Fluid template, you can display an image.

The relevant Extbase controller part:

**EXT:my_extension/Classes/Controller/BlogController.php**

```php
<?php

declare(strict_types=1);

namespace MyVendor\MyExtension\Controller;

use MyVendor\MyExtension\Domain\Model\Blog;
use MyVendor\MyExtension\Domain\Repository\BlogRepository;
use Psr\Http\Message\ResponseInterface;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;

class BlogController extends ActionController
{
    public function __construct(protected readonly BlogRepository $blogRepository)
    {
        // Note: The repository is a standard extbase repository, nothing specific
        //       to this example.
    }

    public function showAction(Blog $blog): ResponseInterface
    {
        $this->view->assign('blog', $blog);

        return $this->htmlResponse();
    }
}

```

and the corresponding Fluid template:

**EXT:my_extension/Resources/Private/Templates/Blog/Show.html**

```html
<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers"
      data-namespace-typo3-fluid="true">

<f:layout name="Default" />

<f:section name="main">
    <p>Single image:</p>
    <f:image image="{blog.singleFile.originalFile}" />

    <p>Multiple images:</p>
    <f:for each="{blog.multipleFiles}" as="image">
        <f:image image="{image.originalFile}" />
    </f:for>

    <p>Access first image of multiple images:</p>
    <f:image image="{blog.multipleFiles[0].originalFile}" />
</f:section>

```

On the PHP side within controllers, you can use the usual
`$blogItem->getSingleFile()` and `$blogItem->getMultipleFiles()`
Extbase getters to retrieve the FileReference object.

## Writing FileReference entries {#extbase-fileupload-writing}

### Manual handling {#extbase-fileupload-writing-manual}

> [!TIP]
> **Hint**
>
> With TYPO3 v13.3
> [Feature: #103511 - Introduce Extbase file upload and deletion handling](https://docs.typo3.org/c/typo3/cms-core/main/en-us/Changelog/13.3/Feature-103511-IntroduceExtbaseFileUploadHandling.html#feature-103511-1711894330)
> was introduced and allows a simplified file upload handling. See
> [Manual handling](https://docs.typo3.org/m/typo3/reference-coreapi/13.4/en-us/ExtensionArchitecture/Extbase/Reference/FileUpload.html#extbase_fileupload_writing-manual) for details.

With TYPO3 versions 12.4 and below, attaching files to an Extbase domain model
is possible by either:

-   Manually evaluating the `$_FILES` data, process and validate the data,
    use raw QueryBuilder write actions on `sys_file` and `sys_file_reference`
    to persist the files quickly, or use at least some API methods:

    **EXT:my_extension/Classes/Controller/BlogController.php, excerpt**

    ```php
    <?php

    declare(strict_types=1);

    namespace MyVendor\MyExtension\Controller;

    use MyVendor\MyExtension\Domain\Model\Blog;
    use MyVendor\MyExtension\Domain\Repository\BlogRepository;
    use TYPO3\CMS\Core\Resource\DuplicationBehavior;
    use TYPO3\CMS\Core\Resource\ResourceFactory;
    use TYPO3\CMS\Core\Utility\GeneralUtility;
    use TYPO3\CMS\Core\Utility\StringUtility;
    use TYPO3\CMS\Extbase\Domain\Model\FileReference;
    use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;

    class BlogController extends ActionController
    {
        public function __construct(
            protected ResourceFactory $resourceFactory,
            protected BlogRepository $blogRepository,
        ) {}

        public function attachFileUpload(Blog $blog): void
        {
            $falIdentifier = '1:/your_storage';
            $yourFile = '/path/to/uploaded/file.jpg';

            // Attach the file to the wanted storage
            $falFolder = $this->resourceFactory->retrieveFileOrFolderObject($falIdentifier);
            $fileObject = $falFolder->addFile(
                $yourFile,
                basename($yourFile),
                DuplicationBehavior::REPLACE,
            );

            // Initialize a new storage object
            $newObject = [
                'uid_local' => $fileObject->getUid(),
                'uid_foreign' => StringUtility::getUniqueId('NEW'),
                'uid' => StringUtility::getUniqueId('NEW'),
                'crop' => null,
            ];

            // Create the FileReference Object
            $fileReference = $this->resourceFactory->createFileReferenceObject($newObject);

            // Port the FileReference Object to an Extbase FileReference
            $fileReferenceObject = GeneralUtility::makeInstance(FileReference::class);
            $fileReferenceObject->setOriginalResource($fileReference);

            // Persist the created file reference object to our Blog model
            $blog->setSingleFile($fileReferenceObject);
            $this->blogRepository->update($blog);

            // Note: For multiple files, a wrapping ObjectStorage would be needed
        }
    }

    ```

    Instead of raw access to `$_FILES`, starting with TYPO3 v12 the recommendation
    is to utilize the [UploadedFile objects instead of $\_FILES](https://docs.typo3.org/c/typo3/cms-core/main/en-us/Changelog/12.0/Breaking-97214-UseUploadedFileObjectsInsteadOf_FILES.html#breaking-97214).
    In that case, validators can be used for custom UploadedFile objects to specify restrictions
    on file types, file sizes and image dimensions.
-   Using (or better: adapting) a more complex implementation by using Extbase TypeConverters,
    as provided by [Helmut Hummel's EXT:upload_example](https://github.com/helhum/upload_example).
    This extension is no longer maintained and will not work without larger adaptation for
    TYPO3 v12 compatibility.
