web-dev-qa-db-fra.com

Téléchargement de fichier Symfony2 étape par étape

J'apprends encore Symfony2 et je ne comprends pas comment télécharger un fichier.

Ne vous inquiétez pas, j'ai déjà vérifié la documentation . C'est vraiment bien, mais mon problème n'est expliqué dans aucun tutoriel.

Je cherche des conseils sur la façon de télécharger un fichier avec Symfony2 mais avec tout ce dont tout le monde a besoin (comme une contrainte d'extension, renommer le fichier en fonction de son identifiant et de son contenu, stocker le chemin dans la base de données, etc ...)

J'ai trouvé de bons tutoriels, j'ai essayé de les mélanger mais sans succès. Chaque fois qu'un problème différent apparaît: re-upload de fichier à chaque soumission sur le formulaire (même si le champ de fichier est vide), supposeeExtension impossible à utiliser, chemin tmp stocké dans la base de données au lieu du chemin de droite, fichier non déplacé, impossible à utilisé l'identifiant dans le changement de nom car l'identifiant est auto-incrémenté et donc pas encore généré).

Donc, je vais mettre une entité 'standard', disons: 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
}

et le contrôleur:

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())
    );
}

Savez-vous maintenant quelles sont les fonctions "importantes" à ajouter pour pouvoir télécharger la photo et la renommer?

Comment vérifiez-vous l'extension pour voir si le téléchargement est possible?

Quelle est votre façon actuelle de faire une telle chose avec Symfony2? Je sais qu'il y a beaucoup de Bundle qui font toutes ces choses pour vous, mais je veux apprendre à le faire et comprendre le processus.

Quel est le moyen "classique" d'implémenter un formulaire de téléchargement de fichier et de renommer une fonction avec Symfony2?

54
Tyrael

Savez-vous maintenant quelles sont les fonctions "importantes" à ajouter pour pouvoir télécharger la photo et la renommer?

Voir le documentation officielle pour savoir comment faire cela. Il existe de bons exemples de travail pour un téléchargement de fichier simple. Consultez également la doctrine documentation pour rappels de cycle de vie) .

Comment vérifiez-vous l'extension pour voir si le téléchargement est possible?

Il existe une validation HTML-Form dans chaque navigateur. Voir cette question pour l'attribut HTML accept="" Dans les éléments input. Également dans Symfony2, vous pouvez spécifier le type MIME d'un fichier téléchargé à l'aide de cette annotation:

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

Même si vous ne voulez utiliser aucun paquet, je vais devoir vous recommander le KnpDoctrineBehavioursBundle qui facilite le téléchargement de fichiers.


Pas à pas:

Comme vous avez déjà lu la documentation, je vais vous donner un exemple de code étape par étape.

Tout d'abord, vous avez besoin d'une entité. Appelons cela Image:

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

Notez l'annotation @ORM\HasLifecycleCallbacks. C'est très important et vous en aurez besoin plus tard. Nous créons tous les champs de base comme ID et tout le reste. Nous avons également besoin d’un champ pour stocker le chemin du fichier dans:

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

Et un pour l'image elle-même. Ici, nous définissons également la validation des images. Dans mon exemple, il doit s'agir de 5M Big et de l'un des mimeTypes définis. Cela devrait être explicite. Sinon, les documents officiels aident comme toujours.

    /**
     * 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;

Ajoutez tous les Getters & Setters Et mettez à jour votre schéma de base de données avec cette commande:

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

Ensuite, nous avons besoin des cycles de vie . Ce sont des méthodes du Entity qui sont appelées pour certains événements. Par exemple, l'annotation @ORM\PreUpdate() avant qu'une méthode indique que cette méthode est appelée juste avant la mise à jour de l'entité.

/**
 * 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();
    }
}

Avant que l'entité soit stockée ou mise à jour, cette méthode est appelée. Vous pouvez l’utiliser pour, par exemple, générer un nom de fichier unique.

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

Appelé avant que l'entité soit supprimée. Cela vous donne le temps de supprimer l'image de vos dossiers ou de consigner un message si vous le souhaitez.

/**
 * 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;
}

C'est la partie importante où votre fichier est réellement déplacé vers le bon répertoire. Notez que j'ai utilisé des méthodes supplémentaires. Vous pouvez tous les obtenir à partir du document officiel .

La prochaine chose dont vous avez besoin est un formulaire. La classe de formulaire elle-même est très simple. Assurez-vous simplement que vous définissez la valeur par défaut data_class Comme ceci:

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

Un champ de téléchargement de fichier peut être créé très facilement dans la méthode buildForm():

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

Les méthodes pour votre Controller sont un peu longues pour simplement les coller ici et à mon humble avis cela ne fait pas partie de la réponse à votre question. Il existe d'innombrables exemples pour la rédaction d'un Controller Action Approprié à vos besoins.


Plus de choses à garder à l'esprit:

  • Vous devez donner à votre app des autorisations d'écriture sur les dossiers dans lesquels vous téléchargez les fichiers. Bien que cela semble évident, il peut être ennuyeux d’exécuter l’application sur plusieurs serveurs.
  • Il existe également un Image Constraint Pour votre entité. Vous pouvez le trouver ici . Mais puisque vous parliez d'un fichier , j'ai utilisé le fichier File Constraint À la place.
  • Comme je l'ai mentionné en haut de cet article, de nombreux bundles gèrent toutes ces choses pour vous. Découvrez-les si vous voulez une vie facile.

Modifier:

  • Passé de DoctrineExtensionsBundle à DoctrineBehaviours puisque le développement sur l'ancien s'est arrêté en faveur du bundle DoctrineBehaviours.
109
ferdynator

Je vous recommande d'utiliser vlabs media bundle .

13
Sidali Hallak

VichUploaderBundle est également facile à utiliser pour le téléchargement de fichiers:

https://github.com/dustin10/VichUploaderBundle

6
Tim Hovius