PHPackages                             dmitriynet/symfony3-file-uploader-bundle - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [File &amp; Storage](/categories/file-storage)
4. /
5. dmitriynet/symfony3-file-uploader-bundle

ActiveSymfony-bundle[File &amp; Storage](/categories/file-storage)

dmitriynet/symfony3-file-uploader-bundle
========================================

Multiple file uploads for Symfony2 with the BlueImp uploader. Also scales uploaded images

v1.0.1(8y ago)02.4k↓100%1MITPHPPHP &gt;=5.6.2

Since Aug 24Pushed 8y ago1 watchersCompare

[ Source](https://github.com/dmitriynet/symfony3-file-uploader-bundle)[ Packagist](https://packagist.org/packages/dmitriynet/symfony3-file-uploader-bundle)[ Docs](https://github.com/dmitriynet/symfony3-file-uploader-bundle)[ RSS](/packages/dmitriynet-symfony3-file-uploader-bundle/feed)WikiDiscussions master Synced 1mo ago

READMEChangelog (2)Dependencies (1)Versions (3)Used By (0)

PunkAveFileUploaderBundle
=========================

[](#punkavefileuploaderbundle)

Maintainer Needed!
==================

[](#maintainer-needed)

This bundle is no longer actively updated. It is maintained for bugs affecting use with Symfony 2.0.x only. If you are interested in taking over maintenance of this bundle, please contact . Thanks!

Introduction
============

[](#introduction)

This bundle provides multiple file uploads, based on the [BlueImp jQuery file uploader](https://github.com/blueimp/jQuery-File-Upload/) package. Both drag and drop and multiple file selection are fully supported in compatible browsers. We chose BlueImp because it has excellent backwards and forwards browser compatibility.

This bundle is a fairly thin wrapper because the existing PHP uploader class provided by BlueImp is very good already and does so many excellent things straight out of the box. We provided a way to integrate it into a Symfony 2 project.

The uploader delivers files to a folder that you specify. If that folder already contains files, they are displayed side by side with new files, as existing files that can be removed.

The bundle can automatically scale images to sizes you specify. The provided synchronization methods make it possible to create forms in which attached files respect "save" and "cancel" operations.

Note on Internet Explorer
=========================

[](#note-on-internet-explorer)

Versions of Internet Explorer prior to 10 have no support for multiple file uploads. However IE users will be able to add a single file at a time and will still be able to build a collection of attached files.

Requirements
============

[](#requirements)

- Symfony3
- jQuery
- jQuery UI
- Underscore

Installation
============

[](#installation)

Symfony 2.0
-----------

[](#symfony-20)

1. Add the following line to your Symfony2 deps file:

    \[FileUploaderBundle\] git=target=/bundles/PunkAve/FileUploaderBundle
2. Modify your AppKernel with the following line:

    new PunkAve\\FileUploaderBundle\\PunkAveFileUploaderBundle(),
3. If you do not already have it, add the following line to your autoload.php file:

    'PunkAve' =&gt; **DIR**.'/../vendor/bundles',
4. Install your vendors:

    bin/vendors install

Symfony 3.\*
------------

[](#symfony-3)

1. Add the following line to your composer.json require block: "dmitriynet/symfony3-file-uploader-bundle": "dev-master"

    The standard symfony 3 composer.json file has a branch alias that interferes with installing this bundle. You can work around by removing the lines

```
 "branch-alias": {
            "dev-master": "3.0-dev"
        }

```

2. Modify your AppKernel with the following line:

    new PunkAve\\FileUploaderBundle\\PunkAveFileUploaderBundle(),
3. Execute composer install

Usage
=====

[](#usage)

Your page must contain Underscore templates to render the file list and uploader. You can use our templates like this:

```
{# Underscore templates for the uploader #}
{% include "PunkAveFileUploaderBundle:Default:templates.html.twig" %}

```

It is sufficient to do so anywhere in the body. You can copy and modify templates.html.twig if you wish and include it from your own directory. Just don't remove the data-\* attributes. The rest of the markup is up to you.

In the Edit Action
==================

[](#in-the-edit-action)

Let's assume you have an editAction() method in a controller. You have a form in which you would like to include a list of attached files that work like other fields in the form: you can add more, you can remove existing files, but nothing permanent happens unless the user clicks "save."

The FileUploader service needs a unique folder name for the files attached to a given object. To accomplish this for new objects as well as existing objects, we suggest you follow the "editId pattern," in which a form is assigned a unique, random "editId" for its entire lifetime, including multiple passes of validation if necessary. This allows us to manage file uploads for new objects that don't have their own id yet.

This code takes creat of creating an editId on the first pass through the form and syncs existing files attached to an existing object, if any. The from\_folder and to\_folder objects specify subdirectories where the attached files will be stored. Later we'll look at how the parent directories of these folders are determined.

(Fetching $posting and validating that the user is allowed to edit that posting is up to you.)

```
$request = $this->getRequest();

$editId = $this->getRequest()->get('editId');
if (!preg_match('/^\d+$/', $editId))
{
    $editId = sprintf('%09d', mt_rand(0, 1999999999));
    if ($posting->getId())
    {
        $this->get('punk_ave.file_uploader')->syncFiles(
            array('from_folder' => 'attachments/' . $posting->getId(),
              'to_folder' => 'tmp/attachments/' . $editId,
              'create_to_folder' => true));
    }
}

```

If the user encounters a validation error on their first attempt to complete the action (for instance, a form validation error), you'll want to present the same list of files again. So use the `getFiles` method to obtain a list of existing files. Make sure you pass that list to your template.

```
$existingFiles = $this->get('punk_ave.file_uploader')->getFiles(array('folder' => 'tmp/attachments/' . $editId));

```

(Note that the editId you generate should be highly random to prevent users from gaining control of each other's attachments.)

When the user saves the form and you have just persisted the posting object, you should also sync files back from the temporary folder associated with the editId to the permanent one associated with the posting's id. Since we are done with the temporary folder we ask the file uploader service to remove that folder. We also ask the service to create the destination folder if necessary:

```
$fileUploader = $this->get('punk_ave.file_uploader');
$fileUploader->syncFiles(
    array('from_folder' => '/tmp/attachments/' . $editId,
    'to_folder' => '/attachments/' . $posting->getId(),
    'remove_from_folder' => true,
    'create_to_folder' => true));

```

Later you can easily obtain a list of the names of all files attached to an object:

```
$files = $fileUploader->getFiles(array('folder' => 'attachments/' . $posting->getId()));

```

However there is a performance cost associated with accessing the filesystem. You will find it more efficient to keep a list of attachments in a Doctrine table, especially if you want to include the first attachment in a list view. Just use getFiles to get the list of filenames and mirror that in your database as you see fit.

In Your Layout
==============

[](#in-your-layout)

To make the necessary JavaScript available via Assetic (note that you must supply jQuery, jQuery UI and Underscore):

```
{% javascripts
    '@MyBundle/Resources/public/js/jquery-1.7.2.min.js'
    '@MyBundle/Resources/public/js/jquery-ui-1.8.22.custom.min.js'
    '@MyBundle/Resources/public/js/underscore-min.js'
    '@PunkAveFileUploaderBundle/Resources/public/js/jquery.fileupload.js'
    '@PunkAveFileUploaderBundle/Resources/public/js/jquery.iframe-transport.js'
    '@PunkAveFileUploaderBundle/Resources/public/js/FileUploader.js' %}

{% endjavascripts %}

```

You must include the iframe transport for compatibility with IE 9 and below.

In the Edit Template
====================

[](#in-the-edit-template)

Let's assume there is an edit.html.twig template associated with the edit action. Here's what it might look like. Note that the render call in your action would pass in the posting object, the editId, the existingFiles array and the isNew flag:

```
{% extends "MyBundle:Default:layout.html.twig" %}

{% block body %}

{# Underscore templates for the uploader #}
{% include "PunkAveFileUploaderBundle:Default:templates.html.twig" %}

    {{ form_widget(form) }}

    {# Hydrated by javascript #}

    {{ isNew ? "Save New Listing" : "Save Changes" }}
    Cancel
    {% if not isNew %}
        Delete
    {% endif %}

// Enable the file uploader

$(function() {
    new PunkAveFileUploader({
        'uploadUrl': {{ path('upload', { editId: editId }) | json_encode | raw }},
        'viewUrl': {{ ('/uploads/tmp/attachments/' ~ editId) | json_encode | raw }},
        'el': '.file-uploader',
        'existingFiles': {{ punkave_get_files('tmp/attachments/' ~ editId) | json_encode | raw }},
        'delaySubmitWhileUploading': '.edit-form'
    });
});

{% endblock %}

```

Progress Display
================

[](#progress-display)

There is a simple spinner in template.html.twig. If you choose to provide your own Underscore templates you can replace it. Just make sure you have your own element with a data-spinner="1" attribute.

If you are using template.html.twig, note that you must publish your assets in the usual way for the spinner image to be available:

```
php app/console assets:install web/

```

As an alternative, you can write your own code on an interval timer that checks whether the `uploading` property of the PunkAveFileUploader object is currently set to true and display a spinner on that basis.

Delaying Form Submission Until Uploads Complete
===============================================

[](#delaying-form-submission-until-uploads-complete)

It's not a good idea to let the user submit a form that the file uploader is meant to be part of if uploads are still in progress. You can easily block this by specifying the 'delaySubmitWhileUploading' option as shown above when creating the PunkAveFileUploader JavaScript object:

```
'delaySubmitWhileUploading': '.edit-form'

```

Alternatively, you can check the `uploading` property of the object you create with `new PunkAveFileUploader(...)` at any time. It will be true if an upload is in progress. The existing implementation of `delaySubmitWhileUploading` relies on this.

In the Upload Action
====================

[](#in-the-upload-action)

In addition to the regular edit action of your form, there must be an upload action to handle file uploads. This action will call the handleFileUpload method of the service to pass on the job to BlueImp's UploadHandler class. Since that class implements the entire REST response directly in PHP, the method does not return.

Here is the upload action:

```
/**
 *
 * @Route("/upload", name="upload")
 * @Template()
 */
public function uploadAction()
{
    $editId = $this->getRequest()->get('editId');
    if (!preg_match('/^\d+$/', $editId))
    {
        throw new Exception("Bad edit id");
    }

    $this->get('punk_ave.file_uploader')->handleFileUpload(array('folder' => 'tmp/attachments/' . $editId));
}

```

This single action actually implements a full REST API in which the BlueImp UploadHandler class takes care of uploading as well as deleting files.

Again, handleFileUpload DOES NOT RETURN as the response is generated in native PHP by BlueImp's UploadHandler class.

Setting the allowed file types
------------------------------

[](#setting-the-allowed-file-types)

You can specify custom file types to divert from the default ones (which are defined in Resources/config/services.yml) by either specifing them in the handleFileUpload method or parameters.yml.

***In the handleFileUpload:***

```
$this->get('punk_ave.file_uploader')->handleFileUpload(array(
    'folder' => 'tmp/attachments/' . $editId,
    'allowed_extensions' => array('zip', 'rar', 'tar')
));

```

In this case the FileUploader service will merge the default extensions with the supplied extensions and make a single regex of it. Using regular expression characters could result in errors.

***Parameters.yml:***If you have the Symfony standard edition installed you can specify them in app/config/parameters.yml:

```
file_uploader.allowed_extensions:
    - zip
    - rar
    - tar

```

Doing this will override the default extensions instead of adding them!

Removing Files
==============

[](#removing-files)

Sooner or later the posting is deleted and you want all of the attachments to be deleted as well.

You can do this as follows:

```
$this->get('punk_ave.file_uploader')->removeFiles(array('folder' => 'attachments/' . $posting->getId()));

```

You might want to do that in a manager class or a doctrine event listener, but that's not our department.

Removing Temporary Files
========================

[](#removing-temporary-files)

If you choose to follow our editId pattern, you'll want to purge contents of web/uploads/tmp that are over a certain age on a periodic basis. People walk away from websites a lot, so not everyone will click your thoughtfully provided "cancel" action that calls removeFiles() based on the editId pattern.

Consider installing this shell script as a cron job to be run nightly. This shell script deletes files more than one day old, then deletes empty folders:

```
#!/bin/sh
find /path/to/my/project/web/uploads/tmp -mtime +1 -type f -delete
find /path/to/my/project/web/uploads/tmp -mindepth 1 -type d -empty -delete

```

(Since the second command is not recursive, the parent folders may stick around an extra day, but they are removed the next day.)

Configuration Parameters
========================

[](#configuration-parameters)

See `Resources/config/services.yml` in this bundle. You can easily decide what the parent folder of uploads will be and what file extensions are accepted, as well as what sizes you'd like image files to be automatically scaled to.

The `from_folder`, `to_folder`, and `folder` options seen above are all appended after `file_uploader.file_base_path` when dealing with files.

If `file_uploader.file_base_path` is set as follows (the default):

```
file_uploader.file_base_path: "%kernel.root_dir%/../web/uploads"

```

And the `folder` option is set to `attachments/5` when calling `handleFileUpload`, then the uploaded files will arrive in:

```
/root/of/your/project/web/uploads/attachments/5/originals

```

If the only attached file for this posting is `botfly.jpg` and you have configured one or more image sizes for the `file_uploader.sizes` option (by default we provide several useful standard sizes), then you will see:

```
/root/of/your/project/web/uploads/photos/5/originals/botfly.jpg
/root/of/your/project/web/uploads/photos/5/thumbnail/botfly.jpg
/root/of/your/project/web/uploads/photos/5/medium/botfly.jpg
/root/of/your/project/web/uploads/photos/5/large/botfly.jpg

```

So all of these can be readily accessed via the following URLs:

```
/uploads/photos/5/originals/botfly.jpg

```

And so on.

The original names and file extensions of the files uploaded are preserved as much as possible without introducing security risks.

Limit number of uploads
-----------------------

[](#limit-number-of-uploads)

You can limit the number of uploaded files by setting the `max_no_of_files` property. You could set this in parameters.yml like this:

```
parameters:
  file_uploader.max_number_of_files: 4

```

You'll probably want to add an error handler for this case. In the template where you initialize PunkAveFileUploader set `errorCallback`

```
// Enable the file uploader
$(function() {
  new PunkAveFileUploader({
    // ... other required options,

    'errorCallback': function(errorObj) {
      if (errorObj.error == 'maxNumberOfFiles') {
        alert("Maximum uploaded files exceeded!");
      }
    }
  });
});

```

Limitations
===========

[](#limitations)

This bundle accesses the file system via the `glob()` function. It won't work out of the box with an S3 stream wrapper.

Syncing files back and forth to follow the editId pattern might not be agreeable if your attachments are very large. In that case, don't use the editId pattern. One alternative is to create objects immediately in the database and not show them in the list view until you mark them live. This way your edit action can use the permanent id of the object as part of the `folder` option, and nothing has to be synced. In this scenario you should probably move the attachments list below the form to hint to the user that there is no such thing as "cancelling" those actions.

Notes
=====

[](#notes)

The uploader has been styled using Bootstrap conventions. If you have Bootstrap in your project, the uploader should look reasonably pretty out of the box.

The "Choose Files" button allows multiple select as well as drag and drop.

###  Health Score

31

—

LowBetter than 68% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity19

Limited adoption so far

Community17

Small or concentrated contributor base

Maturity59

Maturing project, gaining track record

 Bus Factor3

3 contributors hold 50%+ of commits

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~103 days

Total

2

Last Release

3080d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/a4d7460dc50baa475f0c6c37c085e4f0e6dc4e4fa8d5c9c9080b91bea7628e80?d=identicon)[dmitriynet](/maintainers/dmitriynet)

---

Top Contributors

[![Tmerc](https://avatars.githubusercontent.com/u/3801572?v=4)](https://github.com/Tmerc "Tmerc (6 commits)")[![EricReiche](https://avatars.githubusercontent.com/u/894987?v=4)](https://github.com/EricReiche "EricReiche (6 commits)")[![psyray](https://avatars.githubusercontent.com/u/1230954?v=4)](https://github.com/psyray "psyray (4 commits)")[![dmitriynet](https://avatars.githubusercontent.com/u/8964911?v=4)](https://github.com/dmitriynet "dmitriynet (3 commits)")[![keesschepers](https://avatars.githubusercontent.com/u/915930?v=4)](https://github.com/keesschepers "keesschepers (2 commits)")[![umpirsky](https://avatars.githubusercontent.com/u/208957?v=4)](https://github.com/umpirsky "umpirsky (2 commits)")[![bftanase](https://avatars.githubusercontent.com/u/1143666?v=4)](https://github.com/bftanase "bftanase (2 commits)")[![vsychov](https://avatars.githubusercontent.com/u/2186303?v=4)](https://github.com/vsychov "vsychov (1 commits)")[![ezmac](https://avatars.githubusercontent.com/u/810629?v=4)](https://github.com/ezmac "ezmac (1 commits)")[![h-nazzal](https://avatars.githubusercontent.com/u/2687764?v=4)](https://github.com/h-nazzal "h-nazzal (1 commits)")[![mente](https://avatars.githubusercontent.com/u/391997?v=4)](https://github.com/mente "mente (1 commits)")[![Metabor](https://avatars.githubusercontent.com/u/2135064?v=4)](https://github.com/Metabor "Metabor (1 commits)")[![smoorhouse11](https://avatars.githubusercontent.com/u/3173586?v=4)](https://github.com/smoorhouse11 "smoorhouse11 (1 commits)")

---

Tags

symfonybundlefileuploadblueimpSymfony Bundleuploaderimage-transformSymfony 3multiple file uploadpunkave

### Embed Badge

![Health badge](/badges/dmitriynet-symfony3-file-uploader-bundle/health.svg)

```
[![Health](https://phpackages.com/badges/dmitriynet-symfony3-file-uploader-bundle/health.svg)](https://phpackages.com/packages/dmitriynet-symfony3-file-uploader-bundle)
```

###  Alternatives

[presta/image-bundle

PrestaImageBundle is a Symfony bundle providing tools to resize uploaded and remote images before sending them through a classic form.

24155.9k](/packages/presta-image-bundle)[ranky/media-bundle

A complete and friendly media file manager for Symfony

681.3k](/packages/ranky-media-bundle)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
