Reputation: 113
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?????
Upvotes: 1
Views: 3687
Reputation: 113
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> 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.
Upvotes: 2