1

I have read questions concerning this topic and also seen responses that have been of importance thus far, but am currently experiencing some programming difficulties related to this same issue. I am trying to upload multiple files using the Symfony 3 framework and it's been a challenge really. I have the following code for doing this; The first is the Form Type am using:

class ProductImageType extends AbstractType
{
/**
* Build the form
* @param None
* @return void
**/
public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder->add('file', FileType::class, array('attr'=>array('class'=>'form-control'), 'multiple' => true));
}

public function setDefaultOptions(OptionsResolverInterface $resolver)
{
    $resolver->setDefaults(
        array(
            'data_class' => 'AppBundle\Entity\ProductImages',
        )
    );
}

public function getName()
{
    return 'ProductImageType';
}
}

This is the Entity Am using:

class ProductImages
{
/**
 * @var int
 *
 * @ORM\Column(name="id", type="integer")
 * @ORM\Id
 * @ORM\GeneratedValue(strategy="AUTO")
 */
private $id;

/**
 * @var string
 *
 * @ORM\Column(name="productSku", type="string", length=15, unique=true)
 */
private $productSku;

/**
 * @var string $file
 *
 * @ORM\Column(name="file", type="string", length=255)
 * @Assert\NotBlank(message="You must select at least one valid image file.")
 * 
 */
private $file;

/**
 * @var int
 *
 * @ORM\Column(name="dateCreated", type="integer", nullable=true)
 */
private $dateCreated;


/**
 * Get id
 *
 * @return int
 */
public function getId()
{
    return $this->id;
}

/**
 * Set productSku
 *
 * @param string $productSku
 *
 * @return productImages
 */
public function setProductSku($productSku)
{
    $this->productSku = $productSku;

    return $this;
}

/**
 * Get productSku
 *
 * @return string
 */
public function getProductSku()
{
    return $this->productSku;
}

/**
 * Set images
 *
 * @param UploadedFile $file
 *
 * @return productImages
 */
public function setFile($file = null)
{
    $this->file = $file;

    return $this;
}

/**
 * Get images
 *
 * @return string
 */
public function getFile()
{
    return $this->file;
}

/**
 * Set dateCreated
 *
 * @param integer $dateCreated
 *
 * @return productImages
 */
public function setDateCreated($dateCreated)
{
    $this->dateCreated = $dateCreated;

    return $this;
}

/**
 * Get dateCreated
 *
 * @return int
 */
public function getDateCreated()
{
    return $this->dateCreated;
}
}

And this is the controller am using to handle the file upload:

public function uploadAction(Request $request)
{
    $files = $request->files->get('product_image');
    $sku = $request->request->get('productSku');
    $uploaded = false; 
    $message = null;

    $count = 0;
    $image_files = [];

    $uploadDir = $this->getParameter('products_images_directory') . DIRECTORY_SEPARATOR . $sku . DIRECTORY_SEPARATOR;
    $mimeTypes = array('image/jpeg','image/jpg','image/png','image/gif','image/bmp');
    $doctrine = $this->getDoctrine()->getManager();

    if(!empty($files))
    {
        foreach($files as $file => $v)
        {
            $filename[$count] =  $sku . '_' . $count . '.' . $v[$count]->guessExtension();
            $image_files[$count]['file'] = $filename[$count];
            $image_files[$count]['file_size'] = $v[$count]->getClientSize();
            Dump($image_files);die;
            /**if(!is_dir($uploadDir) && !file_exists($uploadDir . $filename))
            {
                mkdir($uploadDir, 0775, TRUE);

                if($value[$count]->move($uploadDir, $filename))
                { 
                    $productImages = new ProductImages();

                    $productImages->setProductSku($sku);
                    $productImages->setFile($filename[$i]);
                    $productImages->setDateCreated(strtotime(date('y-m-d h:i:s a')));

                    $doctrine->persist($productImages);
                    $doctrine->flush();
                }

            }
            **/
            $count++;
        }
        Dump($image_files);die('Action ended!');
        if($count>1)
        {   
            $uploaded = TRUE;
            $message = "All Images have been uploaded & saved!!";
        }

    }
    Dump($message);die;
    return (new JsonResponse(
        [
            'uploaded'=>$uploaded,
            'message'=>$message
        ]
    ));

}

I was going to use Dropzone.js to handle the front-end, i had to make sure everything works fine before integrating it. I discovered that when I try uploading multiple images using the foreach(...) only one of the images gets uploaded. on Dump(...) of the content of $request->request->get(...) i see the multiple files that were selected, but the foreach(...) only fetches the content of the first array not the second or third... I know the problem is within this condition, but i can't figure it out. Does someone have the sixth eye to assist, please?????

aknessy
  • 103
  • 1
  • 12
  • You should use the form type to received the data aswell. Can you post only the relevant code? I mean if you have a problem for the foreach, we don't need the code og the entity, we do'nt need the code in comment etc... It is just harder for us to understand and help you. – goto Oct 31 '17 at 09:58
  • Possible duplicate of [Multiple file upload with Symfony2](https://stackoverflow.com/questions/6736946/multiple-file-upload-with-symfony2) – goto Oct 31 '17 at 09:59

1 Answers1

2

After much sleepless hours, i was able to finally get Symfony to work with dropzone.js for multiple (image) file uploads. This might be tweaked to work with other types of files of course:

My Entity:

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;

/**
* productImages
*
* @ORM\Table(name="product_images")
* @ORM\Entity(repositoryClass="AppBundle\Repository\productImagesRepository")
*/

class ProductImages
{

/**
 * @var int
 *
 * @ORM\Column(name="id", type="integer")
 * @ORM\Id
 * @ORM\GeneratedValue(strategy="AUTO")
 */
private $id;

/**
 * @var string
 *
 * @ORM\Column(name="productSku", type="string", length=15, unique=true)
 */
private $productSku;

/**
 *
 * @ORM\Column(name="files", type="string", length=255)
 * @Assert\NotBlank(message="You must select at least one valid image 
 file.")
 * 
 */
private $files;


/**
 * @var int
 *
 * @ORM\Column(name="dateCreated", type="integer", nullable=true)
 */
private $dateCreated;

/**
 * Class Contructor
 * 
 * @param array $options
 * @return void 
 */
public function __construct()
{}

/**
 * Get id
 *
 * @return int
 */
public function getId()
{
    return $this->id;
}

/**
 * Set productSku
 *
 * @param string $productSku
 *
 * @return productImages
 */
public function setProductSku($productSku = NULL)
{
    $this->productSku = $productSku;

    return $this;
}

/**
 * Get productSku
 *
 * @return string
 */
public function getProductSku()
{
    return $this->productSku;
}

/**
 * Set image Files
 *
 * @param String $files
 *
 * @return productImages
 */
public function setFiles($files = NULL)
{
    $this->files = (string)$files;

    return $this;
}

/**
 * Get image Files
 *
 * @return string
 */
public function getFiles()
{
    return $this->files;
}

/**
 * Set dateCreated
 *
 * @param integer $dateCreated
 *
 * @return productImages
 */
public function setDateCreated($dateCreated)
{
    $this->dateCreated = $dateCreated;

    return $this;
}

/**
 * Get dateCreated
 *
 * @return int
 */
public function getDateCreated()
{
    return $this->dateCreated;
}
}

Form Type:

class ProductImageType extends AbstractType
{
/**
* Build the form
* @param None
* @return void
**/
public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder->add('files', FileType::class, array('attr'=>array('class'=>'form-control'), 'multiple' => true));
}

public function setDefaultOptions(OptionsResolverInterface $resolver)
{
    $resolver->setDefaults(
        array(
            'data_class' => 'AppBundle\Entity\ProductImages',
        )
    );
}

public function getName()
{
    return 'ProductImageType';
}

}

Controller Action:

/**
* Upload Product Image(s)
*
* @Route("/admin/products/upload", name="uploadProductImageRoute")
*
* @access public
* @param Request $request
* @return Object
**/
public function uploadInitAction(Request $request)
{

    $files = $request->files->get('files');
    $sku = $request->request->get('productSku');

    $uploaded = false; 
    $message = null;
    $count = $countValid = 0 ;

    $mimeTypes = array('jpeg','jpg','png','gif','bmp');

    if(!empty($files))
    {
        for($count; $count < count($files); $count++)
        {
            if(in_array($files[$count]->guessClientExtension(), $mimeTypes))
                $countValid++;
        }
        if($countValid == count($files))
            $uploaded = $this->uploadExec($sku, $files);
    }

    if($uploaded)
        $message = "All Images have been uploaded & saved!!";
    else 
        $message = "Selected File(s) weren't uploaded!!";


    return $this->json(
        [
            'uploaded' => $uploaded,
            'message' => $message
        ]
    );

}

/**
 * Performs Actual File Upload
 * 
 * @param string $sku
 * @param array $args
 * @return Boolean
 * 
 */
private function uploadExec($sku, $args = array())
{
    /**
     * Make sure this is a new product without images saved yet
     */
    if($this->hasImages($sku))return FALSE;

    $count = 0;
    $image_files = [];
    $doctrine = $this->getDoctrine()->getManager();

    $uploadDir = $this->getParameter('products_images_directory') . DIRECTORY_SEPARATOR . $sku . DIRECTORY_SEPARATOR;

    if(!is_dir($uploadDir))
    {
        mkdir($uploadDir, 0775, TRUE);
    }

    if(!empty($args) && count($args) > 0)
    {
        for($count; $count < count($args); $count++)
        {
            $filename[$count] =  $sku . '_' . $count . '.' . $args[$count]->guessClientExtension();

            if(!file_exists($uploadDir . $filename[$count]))
            {
                if($args[$count]->move($uploadDir, $filename[$count]))
                {  
                    $image_files[$count]['file'] = $filename[$count];
                    $image_files[$count]['file_size'] = $args[$count]->getClientSize();     
                    //$image_files[$count]['file_location'] = $uploadDir;
                }
            }
        }

        $jsonEncodeFiles = json_encode($image_files);
        /*
         * Persist Uploaded Image(s) to the Database
         */
        $productImages = new ProductImages();
        $productImages->setProductSku($sku);
        $productImages->setFiles($jsonEncodeFiles);
        $productImages->setDateCreated(strtotime(date('y-m-d h:i:s a')));

        $doctrine->persist($productImages);
        $doctrine->flush();

        if( NULL != $productImages->getId() )return TRUE;
    }

    return FALSE;
}

Template:

{{ form_start(uploadForm, {'action':path('uploadProductImageRoute'), 'method' : 'POST', 'attr': {'id' : 'form-with-dropzone', 'class' : 'form-horizontal dropzone' }}) }}
<input type="hidden" name="productSku" value="{{ sku }}" />
<div class="row">
    <div class="dropzone-previews"></div>
    <div class="fallback"> 
        {{ form_widget(uploadForm.files) }}
    </div>
</div>
{{ form_end(uploadForm) }}
<div class="row no-margin-right no-margin-left">
<div class="form-group no-margin-right no-margin-left" style="margin-top: 30px;">
  <div class="pull-right">
      <button id="submit" type="submit" class="btn btn-sm btn-inverse"><i class="ace-icon typcn typcn-location-arrow-outline align-top bigger-115"></i>&nbsp;Upload Image(s)</button>
  </div>
  </div>
 </div>

Javascript:

 Dropzone.options.formWithDropzone = {
        autoProcessQueue: false,
        uploadMultiple: true,
        paramName: "files",
        parallelUploads: 10,
        maxFiles: 10,
        addRemoveLinks: true,
        acceptedFiles: 'image/*',
        init: function(){
            var dropZone = this;

            $('#submit').click(function(e){
                e.preventDefault();
                e.stopPropagation();
                dropZone.processQueue();
            });

            dropZone.on("success", function(file, response) {
                if(dropZone.getAcceptedFiles().length > 0){
                    $.gritter.add({
                        title : 'Upload Complete',
                        text : response.message + '\n\nA total of: ' + dropZone.getAcceptedFiles().length + ' images uploaded successfully!',
                        class_name : 'gritter-success'
                    })
                }else{
                    $.gritter.add({
                        title : 'Upload Incomplete',
                        text : response.message,
                        class_name : 'gritter-error'
                    })
                }
     });
   }
}

There was no need of making complex and unnecessary entity relationship mappings to get this to do what i wanted it to. While working on this, i realized that using the getMimeType() method of the UploadedFile class to check the mimetype of the uploaded files(s) resulted in the error: FileNotFoundException in MimeTypeGuesser.php line 123: The file "F:\wamp2.5\tmp\php....tmp" does not exist

However, the error disappeard after i changed the method getMimeType() to guessClientExtension()

In future, i hope this will save someone a lot of time.

aknessy
  • 103
  • 1
  • 12