jbouzekri/file-uploader-bundle
A Symfony bundle that streamlines file uploads tied to Doctrine entities, handling storage, file naming, and lifecycle so you can attach and manage uploaded files with minimal boilerplate.
Read this first: it is a Symfony bundle, not a Laravel package. There is no ServiceProvider, no config/file_uploader.php, no facade, and php artisan vendor:publish will do nothing for you. Installing it into a Laravel app gets you a folder in vendor/ and nothing else — the classes extend Symfony\Bundle\FrameworkBundle\Controller\Controller, hook into the Symfony kernel, and expect AppKernel/bundles.php registration. If you are committed to Laravel, treat this as a reference architecture, not a dependency (see the last pattern under Implementation Patterns for the Laravel-native equivalent).
Assuming you land in a Symfony 3.x/4.x app:
composer require jbouzekri/file-uploader-bundle and register the bundle (AppKernel::registerBundles() or config/bundles.php).Resources/public.vendor/jbouzekri/file-uploader-bundle/DependencyInjection/. The exact key names have drifted across releases — trust the README pinned to your installed tag, not blog posts.1. The upload-then-claim (two-phase) pattern. This is the whole architecture, and everything else is sugar. Phase one: XHR POST to the OneUp endpoint, file lands in a temp dir, JSON response returns the generated filename. Phase two: that filename is written into a hidden input, and on form POST you verify the file exists in your temp namespace, then move/copy it into permanent storage and persist the path on the entity. Never persist the temp path — persist the final one.
2. One mapping per image context. Configure a distinct mapping for avatar, cover, product_gallery, etc. Each mapping pairs an OneUp endpoint (own size limit, own MIME whitelist, own namer/storage) with a Liip filter set (own crop dimensions). This keeps validation tight per context instead of one giant "images" bucket with the loosest limits.
3. Crop as a server-side Liip filter, not client pixels. Let the JS crop UI post either a filter-set name or crop coordinates, and apply the crop through Liip Imagine in your controller/service. Trusting client-supplied x/y/w/h to be sane is how you get 10GB PNG decompression bombs; going through a named filter means the dimensions are fixed in config.
4. Form type + constraint, not raw request handling. Wrap the hidden field in a small custom form type (FileUploaderType) that carries the mapping name, and add a custom constraint (TempFileExists) that asserts the submitted filename matches your generated-name scheme and is still present in the temp dir. That constraint is your single security chokepoint — put all the path-traversal and ownership checks there.
5. Collection handling. For galleries, repeat the widget per collection entry but give each a unique DOM target/mapping name — Blueimp attaches to a container by selector, and two widgets with the same selector silently bind to the first one.
6. Lifecycle cleanup. The bundle does not clean up after you. Add a console command/cron to purge temp uploads older than N hours, and use Doctrine lifecycle callbacks (postRemove) to unlink the permanent files when an entity is deleted. Without this you accumulate orphans forever.
7. If you're actually on Laravel, reproduce this in an afternoon: pion/laravel-chunk-upload (or a hand-rolled UploadTempController) + intervention/image for the crop filter + cropperjs/filepond in the Blade view + storage/app/tmp as the claimable namespace. You get the same two-phase flow, no Symfony kernel, and dependencies that aren't from 2018.
Resources/public publishing, the templating bundle). Expect to fork and patch, not just composer update.bin/console assets:install --symlink; with Webpack Encore you must manually import the Blueimp/Jcrop JS out of vendor/.../Resources/public. Budget real time for this or you'll never see the crop UI.web/. Validate extension and MIME, randomize names (uniqid namer), and never trust the hidden field value without confirming the file exists in your temp dir and was created in this session.liip:imagine:cache:remove, and resolve paths at runtime in Twig so you don't emit stale cached thumbnails.namer or storage service id blows up with a confusing 500 at upload time, not at container compile. Before touching JS, debug:router | grep upload and hit the endpoint with curl -F — if curl works, the problem is front-end.var/logs for the OneUp handler exception (the JS error message is useless) → only then look at the JS.oneup_uploader.post_upload event / UploadListener for claiming and validation, Liip Imagine custom filters for crop logic, and overriding the bundle's Twig widget template for markup. Overriding those three covers ~90% of customization needs without touching vendor code.How can I help you explore Laravel packages today?