Symfony2 загрузка файла шаг за шагом

Я все еще изучаю Symfony2 и не понимаю, как загрузить файл.

Не волнуйтесь, я уже проверил документацию. Это действительно хорошо, но моя проблема не объясняется ни в одном учебнике.

Я ищу руководство о том, как загрузить файл с помощью Symfony2, но со всем, что нужно всем (например, ограничение расширения, переименование файла на основе идентификатора и прочее, сохранение пути в БД и т. Д.)

Я нашел хорошие учебники, пытался смешать их, но безуспешно. Каждый раз, когда появляется другая проблема: файл повторно загружается при каждой отправке в форме (даже если поле файла пусто), невозможно использовать догадку расширения, путь tmp сохраняется в базе данных вместо правильного пути, файл не перемещается, невозможно использовал идентификатор в переименовании, потому что идентификатор находится в автоинкременте и поэтому еще не сгенерирован).

Итак, я поставлю "стандартную" сущность, скажем так: Photo.php

/**
 * Photo
 *
 * @ORM\Table(name="photo")
 * @ORM\Entity
 * @ORM\HasLifecycleCallbacks
 */
class Photo
{
    // Annotation for the id and auto increment etc
    private $id;

    /**
     * @var string
     * @Assert\File( maxSize = "3072k", mimeTypesMessage = "Please upload a valid Image")
     * @ORM\Column(name="image", type="string", length=245, nullable=false)
     */
    private $image

    private $title

    private $description

    // all the function get, set for the 4 previous variables
}

и контроллер:

public function addPhotoAction()
{
    $add_photo = new Photo;
    $formBuilderPhoto = $this->createFormBuilder($add_photo);
    $formBuilderPhoto
        ->add('title','text',array('label'  => 'Title of the photo', 'required' => true))
        ->add('image','file', array('required' => true, 'data_class' => null))
        ->add('description','textarea',array('label' => 'Description of your photo', 'required' => false))
    ;

    $form_photo = $formBuilderPhoto->getForm();

    if ($request->getMethod() == 'POST') {
        $form_photo->bind($request);
        if ($form_photo->isValid()) {
            // ...
        }
    }
    return $this->render('MyBundle:frontend:photo.html.twig',
        array('form_photo' => $form_photo->createView())
    );
}

Знаете ли вы сейчас, какую "важную" функцию добавить, чтобы иметь возможность загружать фотографию и переименовывать ее?

Как проверить расширение, чтобы увидеть, возможна ли загрузка?

Как вы на самом деле делаете такие вещи с Symfony2? Я знаю, что есть много Bundle, которые делают все это за вас, но я хочу научиться делать это и понимать процесс.

Каков "классический" способ реализации формы загрузки файлов и функции переименования с помощью Symfony2?

4 ответа

Решение

Знаете ли вы сейчас, какую "важную" функцию добавить, чтобы иметь возможность загружать фотографию и переименовывать ее?

Смотрите официальную документацию о том, как это сделать. Есть хорошие рабочие примеры для простой загрузки файла. Также проверьте документацию доктрины для обратных вызовов жизненного цикла.

Как проверить расширение, чтобы увидеть, возможна ли загрузка?

В каждом браузере есть некоторая проверка HTML-формы. Смотрите этот вопрос для HTML accept="" приписывать input элементы. Также в Symfony2 вы можете указать MIME-тип загружаемого файла, используя эту аннотацию:

/**
 * @Assert\File(
 *     maxSize = "1024k",
 *     mimeTypes = {"application/pdf", "application/x-pdf"},
 *     mimeTypesMessage = "Please upload a valid PDF"
 * )
 */

Даже если вы не хотите использовать какие-либо пакеты, я порекомендую вам KnpDoctrineBehavioursBundle, который упрощает загрузку файлов.


Шаг за шагом:

Поскольку вы уже прочитали документацию, я приведу пошаговый пример кода.

Прежде всего вам нужна сущность. Давайте назовем это Image:

/**
 * Class Image
 *
 * @ORM\Entity()
 * @ORM\HasLifecycleCallbacks
 */
class Image extends BaseEntity
{

Обратите внимание @ORM\HasLifecycleCallbacks аннотаций. Это очень важно, и вам это нужно позже. Мы создаем все основные поля, такие как ID и что "нет. Также нам нужно поле для хранения пути к файлу:

    /**
     * Image path
     *
     * @var string
     *
     * @ORM\Column(type="text", length=255, nullable=false)
     */
    protected $path;

И один для самого изображения. Здесь мы также определяем валидацию для изображений. В моем примере это должно быть 5M большой и один из определенных mimeTypes, Это должно быть само за себя. В противном случае официальные документы помогают, как всегда.

    /**
     * Image file
     *
     * @var File
     *
     * @Assert\File(
     *     maxSize = "5M",
     *     mimeTypes = {"image/jpeg", "image/gif", "image/png", "image/tiff"},
     *     maxSizeMessage = "The maxmimum allowed file size is 5MB.",
     *     mimeTypesMessage = "Only the filetypes image are allowed."
     * )
     */
    protected $file;

Добавить все Getters & Setters и обновите схему базы данных с помощью этой команды:

php app/console doctrine:schema:update --force

Далее нам нужны жизненные циклы. Это методы в Entity которые призваны на определенные события. Например, @ORM\PreUpdate() аннотация перед методом говорит о том, что этот метод вызывается непосредственно перед обновлением объекта.

/**
 * Called before saving the entity
 * 
 * @ORM\PrePersist()
 * @ORM\PreUpdate()
 */
public function preUpload()
{   
    if (null !== $this->file) {
        // do whatever you want to generate a unique name
        $filename = sha1(uniqid(mt_rand(), true));
        $this->path = $filename.'.'.$this->file->guessExtension();
    }
}

Прежде чем объект будет сохранен или обновлен, вызывается этот метод. Вы можете использовать его, например, для генерации уникального имени файла.

/**
 * Called before entity removal
 *
 * @ORM\PreRemove()
 */
public function removeUpload()
{
    if ($file = $this->getAbsolutePath()) {
        unlink($file); 
    }
}

Вызывается до удаления объекта. Это дает вам время, чтобы удалить изображение из ваших папок или записать сообщение, если хотите.

/**
 * Called after entity persistence
 *
 * @ORM\PostPersist()
 * @ORM\PostUpdate()
 */
public function upload()
{
    // The file property can be empty if the field is not required
    if (null === $this->file) {
        return;
    }

    // Use the original file name here but you should
    // sanitize it at least to avoid any security issues

    // move takes the target directory and then the
    // target filename to move to
    $this->file->move(
        $this->getUploadRootDir(),
        $this->path
    );

    // Set the path property to the filename where you've saved the file
    //$this->path = $this->file->getClientOriginalName();

    // Clean up the file property as you won't need it anymore
    $this->file = null;
}

Это важная часть, где ваш файл фактически перемещается в правильный каталог. Обратите внимание, что я использовал несколько дополнительных методов. Вы можете получить их из официальных документов.

Следующее, что вам нужно, это форма. Сам класс формы очень прост. Просто убедитесь, что вы установили значение по умолчанию data_class как это:

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

Поле загрузки файла может быть создано очень легко в buildForm() метод:

$builder->add('file', 'file');

Методы для вашего Controller немного долго просто вставлять их здесь и ИМХО это не часть ответа на ваш вопрос. Есть бесчисленное множество примеров для написания правильного Controller Action для вашей цели.


Больше вещей, которые вы должны иметь в виду:

  • Вы должны дать свой app разрешения на запись в папки, в которые вы загружаете файлы. Хотя это кажется очевидным, это может раздражать, если у вас есть несколько серверов, на которых вы запускаете приложение.
  • Есть Image Constraint для вашей сущности. Вы можете найти это здесь. Но так как вы говорили о загрузке файла, я использовал File Constraint вместо.
  • Как я упоминал в верхней части этого поста, есть много пакетов, которые обрабатывают все эти вещи для вас. Проверьте их, если вы хотите легкой жизни.

Редактировать:

  • Изменено с DoctrineExtensionsBundle в DoctrineBehaviours так как развитие старого остановилось в пользу DoctrineBehaviours расслоение.

Я рекомендую вам использовать vlabs media bundle.

VichUploaderBundle также прост в использовании для загрузки файлов:

https://github.com/dustin10/VichUploaderBundle

Я рекомендую пакет VichUploader и этот код с пакетом, имплантированным в сущность и FormType.

      composer require vich/uploader-bundle

Допуск.php

      /**
 * @ORM\Entity(repositoryClass=AdmissionRepository::class)
 * @Vich\Uploadable
 */
class Admission
{
    /**
     * @ORM\Id
     * @ORM\GeneratedValue
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @ORM\Column(type="string", length=255)
     */
    private $cin;

    /**
     * @Vich\UploadableField(mapping="product_image", fileNameProperty="cin")
     * @var File
     */
    private $cinFile;


    public function getId(): ?int
    {
        return $this->id;
    }

    public function getCin(): ?string
    {
        return $this->cin;
    }

    public function setCin(string $cin): self
    {
        $this->cin = $cin;

        return $this;
    }
}

Тип допуска.php

         class AdmissionType extends AbstractType
      {
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('cinFile', VichFileType::class);

    }

vich_uploader.yaml

      vich_uploader:
    db_driver: orm

    mappings:
        product_image:
            uri_prefix: /uploads
            upload_destination: '%kernel.project_dir%/public/uploads'
            inject_on_load: false
            delete_on_update: true
            delete_on_remove: true
            namer: vich_uploader.namer_origname
Другие вопросы по тегам