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

51

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

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

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

Я нашел хорошие уроки, попытался их смешивать, но без успеха. Каждый раз, когда возникает другая проблема: перезагрузка файлов на каждой отправке в форме (даже если поле файла пуст), guessExtension невозможно использовать, путь 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?

Теги:
file
formbuilder
upload
doctrine2

3 ответа

102
Лучший ответ

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

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

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

В каждом браузере есть проверка 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

Далее нам нужны lifecycles. Это методы в 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 немного длинны для просто вставки их здесь, и IMHO это не часть ответа на ваш вопрос. Существует множество примеров для написания правильного Controller Action для вашей цели.


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

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

Edit:

  • Изменено от DoctrineExtensionsBundle до DoctrineBehaviours, так как разработка на старой была остановлена ​​в пользу пакета DoctrineBehaviours.
  • 0
    Отлично ! Спасибо, я немного изменил ваш код и, кажется, наконец понял, как он работает. Я сначала попробовал только с путем, но с переменной файла это лучше
  • 3
    Меня привели к этому вопросу по тем же причинам, что и ОП. Этот ответ понятнее официальной документации! Молодец, спустя два года, и он все еще более актуален, чем документы ... Я нашел такие вещи, как единственная проблема с Symfony до сих пор. Это мощный, но документация очень плохая
Показать ещё 3 комментария
13

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

  • 2
    Это работает безупречно - просто и имеет соответствующую документацию. Спасибо!
  • 1
    Я хотел бы дать это больше, чем один голос. Спасибо!
Показать ещё 1 комментарий
6

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

https://github.com/dustin10/VichUploaderBundle

Ещё вопросы

Сообщество Overcoder
Наверх
Меню