intaro/file-uploader-bundle
A Symfony bundle for adding file upload functionality to PHP applications. It provides upload handling, configuration, and integration points for managing uploaded files within Symfony projects, helping developers implement file uploads with less custom boilerplate.
First, decide whether you should use it at all. This is a Symfony2-era bundle, not a Laravel package. If you are working in Laravel, the correct day-to-day tool is Illuminate\Http\UploadedFile plus Storage::putFile() / $request->file('avatar')->store('avatars'). Do not install this bundle into a Laravel app. Use this assessment only if you are maintaining a Symfony application or evaluating a port.
If you are in Symfony:
Check compatibility before installing:
composer show intaro/file-uploader-bundle --all
Inspect its composer.json constraints against your Symfony and PHP versions. The last release date is not proof of modern Symfony support.
Install and register the bundle:
composer require intaro/file-uploader-bundle
Symfony2/3: register new Intaro\FileUploaderBundle\IntaroFileUploaderBundle() in app/AppKernel.php.
Symfony4+: add it to config/bundles.php.
Look first at:
README.mdResources/config/services.yml — find the actual uploader service IDDependencyInjection/Configuration.php — available config keysService/ — upload logicEntity/ — any Doctrine-backed file modelForm/ and Validator/ — if the bundle provides form types or constraintsConfigure upload locations explicitly. Prefer a directory outside the web root and stream downloads through a controller. If you must serve directly, configure a separate public upload directory with no PHP execution.
First use case — avatar upload in a controller:
public function uploadAvatarAction(Request $request)
{
$file = $request->files->get('avatar');
if (!$file) {
throw new BadRequestHttpException('No file uploaded.');
}
// Confirm the real service ID in Resources/config/services.yml.
$uploader = $this->get('intaro.file_uploader');
$path = $uploader->upload($file, 'avatars');
// Persist $path against your user/entity.
$user->setAvatarPath($path);
$em->flush();
return new JsonResponse(['path' => $path]);
}
For a Laravel developer, translate the concepts as follows: Symfony bundle service ≈ Laravel container binding; Doctrine entity ≈ Eloquent model; Symfony form/validator ≈ FormRequest validation; bundle uploader ≈ a thin wrapper around move_uploaded_file.
Wrap the bundle service in your own service. This is the most important pattern. Do not scatter $this->get('intaro.file_uploader') throughout controllers. Inject it into an application service so you can replace it later.
# app/config/services.yml
services:
app.avatar_uploader:
class: AppBundle\Service\AvatarUploader
arguments:
- '@intaro.file_uploader'
- '%avatar_upload_dir%'
class AvatarUploader
{
private $uploader;
private $directory;
public function __construct($uploader, $directory)
{
$this->uploader = $uploader;
$this->directory = $directory;
}
public function upload(UploadedFile $file): string
{
return $this->uploader->upload($file, $this->directory);
}
}
Pattern: upload, persist, clean up on failure. Filesystem moves are not transactional. If Doctrine persistence fails after the file has moved, delete the file in a catch block.
$path = $avatarUploader->upload($file);
try {
$user->setAvatarPath($path);
$em->flush();
} catch (\Exception $e) {
@unlink($absoluteUploadDir . '/' . $path);
throw $e;
}
Pattern: replace-and-delete. When replacing an avatar or attachment, upload the new file first, persist the new path, flush successfully, then delete the old file. Never delete first.
$oldPath = $user->getAvatarPath();
$newPath = $avatarUploader->upload($file);
$user->setAvatarPath($newPath);
$em->flush();
if ($oldPath) {
@unlink($absoluteUploadDir . '/' . $oldPath);
}
Pattern: attach files to Doctrine entities. If the bundle provides a File entity, map it with a OneToOne or OneToMany relation. If it only returns paths, store the path as a string and keep your own metadata columns: original name, MIME type, size, checksum, created timestamp.
/**
* @ORM\OneToOne(targetEntity="Intaro\FileUploaderBundle\Entity\File", cascade={"persist"})
*/
private $avatar;
Verify orphan removal behavior. Doctrine will not delete files from disk automatically. Add a lifecycle listener or explicit service call on entity deletion.
Pattern: multiple uploads. Loop over $request->files->get('attachments'), validate each file independently, upload each to a unique name, and persist all metadata in one transaction where possible. If one file fails, decide whether to roll back the entire batch or keep partial results — document that behavior.
Pattern: custom file names. Use a UUID or content hash instead of the client-supplied name. Preserve the original name only as metadata.
$safeName = bin2hex(random_bytes(16)) . '.' . $file->guessExtension();
If the bundle exposes a naming strategy or event, use it. Otherwise rename the file after upload or wrap the uploader.
Pattern: thumbnails and post-processing. Upload the original first, then dispatch your own event or call an image processor. Keep post-processing asynchronous if thumbnailing is slow. Do not block the HTTP request on large image conversions.
Pattern: serving files. Store outside web/ and stream through a controller with authorization checks. This prevents direct URL access to private uploads and avoids path-guessing attacks.
return $this->file($absolutePath, $downloadName);
Testing pattern. Create a temporary file and pass a real UploadedFile instance; do not mock the filesystem blindly. Assert that the file exists at the expected path and that metadata was persisted. Clean up temporary directories in tearDown().
Do not use this in Laravel. There is no Laravel integration, no service provider, no facade, and no Storage adapter. The bundle is built around Symfony's kernel, DI container, and often Doctrine. Porting it would be more work than using Laravel's native upload and storage APIs.
Compatibility is the first risk. Symfony2 is end-of-life. Check the package's composer.json for symfony/symfony constraints and PHP version requirements. A 2024 release date may only reflect a metadata change or a minor compatibility patch. Test installation in a branch before adopting.
Low adoption means low support. Three stars and zero dependents is a warning sign. Pin the exact version in composer.json, read the source before relying on it, and be prepared to fork or replace it. There is little community knowledge to draw on when something breaks.
Validate file content, not just extensions. Check MIME type with finfo or Symfony's File validator. Enforce maximum size in both PHP (upload_max_filesize, post_max_size) and application code. Reject empty uploads and check $file->getError().
Path traversal and public directory risks. Never concatenate user-supplied filenames into paths. Generate your own names. If uploads are publicly served, ensure the directory cannot execute PHP and cannot be listed.
Config quirks: trailing slashes and absolute paths. Upload directories are a common source of bugs. Normalize paths in one place. Distinguish between the web-relative path stored in the database and the absolute filesystem path used for moving and deleting.
Doctrine does not delete files. Deleting an entity or clearing a relation leaves the file on disk unless you add a listener. Add a preRemove lifecycle callback or a domain event handler that deletes the associated file.
Cache clearing after config changes. Symfony caches container configuration. After changing bundle config or service definitions, clear the cache:
php app/console cache:clear
# or
bin/console cache:clear
Debugging checklist for failed uploads:
UploadedFile error code.upload_tmp_dir.Resources/config/services.yml; bundle service IDs are not guaranteed to match the README.Extension points to prefer:
Verdict for day-to-day use: reasonable only as a small, pinned dependency in a legacy Symfony application where it already exists. Not a strategic choice for new Symfony work, and not applicable to Laravel. For new projects, use VichUploaderBundle on Symfony or Laravel's native Storage/UploadedFile APIs.
How can I help you explore Laravel packages today?