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
расслоение.
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