Symfony HttpFoundation UploadedFile "not uploaded due to unknown error" when using Doctrine DataFixtures

13,790

Solution 1

Thanks to stof, the solution is to make Attachment::setFile() (or Document::setFile() if using the cookbook example) hint for an instance of UploadedFile's parent class, Symfony\Component\HttpFoundation\File\File, and in the fixtures class, create a new instance and pass it to the setFile method

Attachment.php

<?php

namespace Acme\DemoBundle\Entity;

use Symfony\Component\HttpFoundation\File\File;
//...

class Attachment
{
    /**
     * Sets file.
     *
     * @param File $file
     */
    public function setFile(File $file = null)
    {
        $this->file = $file;
        // check if we have an old image path
        if (isset($this->path)) {
            // store the old name to delete after the update
            $this->temp = $this->path;
            $this->path = null;
        } else {
            $this->path = 'initial';
        }
    }

    //...
}

AttachmentFixtures.php

<?php

namespace Acme\DemoBundle\DataFixtures\ORM;

use Symfony\Component\HttpFoundation\File\File;
//...

class AttachmentFixtures //...
{
    //...

    public function load(ObjectManager $manager)
    {
        //...
        $imageFile = new File($pathToCopiedFile);

        $imageAttachment = new Attachment();

        $imageAttachment->setFile($imageFile);
        //...
    }
}

Solution 2

There is now a better solution:

The constructor of UploadedFile has a boolean $test parameter which disables the check using is_uploaded_file. This parameter has been added for testing/fixture code.

Just set it to true and the isValid() check of UploadedFile will not be a problem anymore.

Example:

// My data fixture code.
$test = true;
$userPhoto->setImageFile(new UploadedFile($photoDir . $photoFile, $photoFile, null, null, null, $test));
Share:
13,790
Adam Elsodaney
Author by

Adam Elsodaney

Half-English + half-Egyptian, though I am mostly just an Englishman... I require milk in my tea. I regularly get my hands dirty and wear all development hats: Backend, Frontend, Database, DevOps, Design, UI, UX, QA, etc. With my core strength as a Symfony web developer. I was originally trained as an architect, but soon discovered I had a knack for web development through building by own site to promote my design work. Find me on SensioLabs Connect | Github (as myself) | Github (as ArchFizz) and on LinkedIn | Careers 2.0

Updated on June 23, 2022

Comments

  • Adam Elsodaney
    Adam Elsodaney almost 2 years

    I've been using my Attachment entity based on the cookbook recipie How To Handle File Uploads With Doctrine in Symfony 2.3.

    It works well, even in functional tests. However using it with Doctrine DataFixtures is causing me problems.

    [Symfony\Component\HttpFoundation\File\Exception\FileException]
    The file "o-rly-copy.jpg" was not uploaded due to an unknown error.

    This was not helpful, however I did run php app/console doctrine:fixtures:load -v to bring up a stack trace and it appears the exception is thrown not on the persisting method, but on $manager->flush()

    Attachment::setFile() requires an instance of UploadedFile so I wonder if there is a way round that.

    It appears the error occurs on line 225 of Symfony\Component\HttpFoundation\File\UploadedFile

    return $this->test ? $isOk : $isOk && is_uploaded_file($this->getPathname())
    

    The condition for is_uploaded_file() returns false because the file was already on the server.

    <?php
    
    /**
     * Prepopulate the database with image attachments.
     */
    final class AttachmentFixtures extends AbstractFixture implements OrderedFixtureInterface, ContainerAwareInterface
    {
        private static $imageData = array(
            array(
                'name' => "O RLY?",
                'file' => "o-rly",
                'type' => "jpg",
            ),
            //...
        );
    
        public function getPathToImages()
        {
            return $this->container->get('kernel')->getRootDir() . '/../src/Acme/DemoBundle/Resources/public/default/images';
        }
    
        public function getPathToUploads()
        {
            return $this->container->get('kernel')->getRootDir() . '/../web/uploads/fixtures';
        }
    
        /**
         * {@inheritDoc}
         */
        public function load(ObjectManager $manager)
        {
            $imageReferences = array();
            $filesystem = $this->container->get('filesystem');
    
            foreach (self::$imageData as $image) {
                $imageFilename         = sprintf('%s.%s',      $image['file'], $image['type']);
                $copiedImageFilename   = sprintf('%s-copy.%s', $image['file'], $image['type']);
    
                $pathToImageFile = sprintf('%s/%s', $this->getPathToImages(), $imageFilename);
    
                try {
                    $filesystem->copy($pathToImageFile, $pathToCopiedFile = sprintf('%s/%s', $this->getPathToUploads(), $copiedImageFilename));
                    $filesystem->chmod($pathToCopiedFile, 0664);
                } catch (IOException $e) {
                    $this->container->get('logger')->err("An error occurred while copying the file or changing permissions.");
                }
    
                $imageFile = new UploadedFile(
                    $pathToCopiedFile,                                              // The full temporary path to the file
                    $copiedImageFilename,                                           // The original file name
                    'image/' . 'jpg' === $image['type'] ? 'jpeg' : $image['type'],  // Mime type - The type of the file as would be provided by PHP
                    filesize($pathToCopiedFile),
                    null,
                    null,
                    true
                );
    
                $imageAttachment = new Attachment();
    
                $imageAttachment->setName($image['name']);
                $imageAttachment->setFile($imageFile);
    
                // Populate a reference array for later use
                $imageReferences['attachment-'.$image['file']] = $imageAttachment;
    
                $manager->persist($imageAttachment);
            }
    
            $manager->flush(); // <-- Exception throw here
    
    
            // Create references for each image to be used by other entities that
            // maintain a relationship with that image.
            foreach ($imageReferences as $referenceName => $image) {
                $this->addReference($referenceName, $image);
            }
        }
    }
    
  • Yassine CHABLI
    Yassine CHABLI about 6 years
    this is gonna delete the original file .while you have to deal with the tmp file .
  • famas23
    famas23 about 4 years
    if test = true, then the original file will not be deleted ... In cas we want to delete the file ?