Sous Laravel, le téléversement d'un fichier par programmation est un vrai charme.
Voici les grandes étapes à réaliser.
Il faut tout d'abord créer le formulaire qui permettra de téléverser le fichier.
Ex :
<form method="post" action="{{ route('produits.store') }}" enctype="multipart/form-data">
@csrf
...
<div class="form-group row">
<label for="photo" class="col-form-label col-sm-2">* Photo : </label>
<div class="col-sm-10">
<input id="photo" name="photo" type="file">
</div>
</div>
<div class="form-group row">
<div class="col-sm-2"></div>
<div class="col-sm-10">
<input class="btn btn-primary" type="submit" value="Enregistrer">
</div>
</div>
</form>

Afin de s'assurer que le fichier soumis est valide, il faut effectuer plusieurs vérifications :
Ces validations peuvent être réalisées facilement à l'aide du fichier de validation (classe dérivée de FormRequest).
Ex :
class ProduitRequest extends FormRequest
{
...
public function rules()
{
// taille maximale du fichier en octets
// la validation du mime ne vérifie pas seulement l'extension, elle lit le contenu du fichier pour vérifier le type MIME réel.
$rules = [
...
'photo' => 'required|image|mimes:png,jpeg,jpg,gif|max:4096',
];
return $rules;
}
}
Il est bon d'indiquer dans le formulaire la taille maximale autorisée ainsi que les autres restrictions et ce, avant-même que l'usager choisisse son fichier. Ceci peut être fait à l'aide d'un libellé situé à droite, au-dessus ou en-dessous du contrôle <input type="file">.
Voici une série de fichiers à utiliser lors de vos tests de téléversement pour vous assurer que la validation du fichier à téléverser soit bien faite : FichiersPourTesterFileUpload.zip.
Plus de détails sur la validation des images ici : Valider une image (Vous ne possédez pas les droits pour accéder à cette fiche.).
Normalement, quand une validation échoue, le formulaire est réaffiché avec les valeurs qui y avaient été entrées. On dira que les balises sont repeuplées avec l'ancienne valeur ou, en anglais, « repopulated with old value ».
Par exemple, pour une boîte de saisie, on réaffichera la valeur saisie comme suit :
Ex : <input name="code" type="text" value="{{ old('code') }}">).
Pour une balise <input type="file">, ceci n'est pas possible. Et par chance car cela pourrait causer un important problème de sécurité : un serveur pourrait ainsi pousser un fichier sur le poste du client. Ouch !
Pourtant, si on entre un fichier qui est valide mais qu'une autre information du formulaire ne passe pas à la validation, on devra entrer à nouveau le fichier. Ceci est loin d'être idéal.
Il faut donc développer une stratégie pour contourner ce problème, et une stratégie toute simple passe par la validation JavaScript.
On le sait, tout formulaire Web devrait implanter une validation côté client, en plus de la validation côté serveur, afin d'améliorer l'expérience utilisateur. Dans le cas d'un formulaire avec un <input type="file">, ceci est encore plus vrai. En effet, tant que le navigateur n'a pas fait de postback au serveur, la valeur du fichier à téléverser demeure dans le formulaire. Le fait d'effectuer toutes les validations côté client permettra à l'usager de ne soumettre qu'un formulaire valide, ce qui évite d'avoir à composer avec la problématique du <input type="file">.
La façon la plus simple d'implanter la validation côté client consiste à utiliser Laravel 5 Javascript Validation.
Une fois le fichier sélectionné et validé, on peut procéder à son téléversement sur le serveur.
Par sécurité, l'opération de téléversement sera effectuée dans un try...catch. En effet, de nombreux facteurs externes au site Web pourraient empêcher le téléversement du fichier : problème de droits sur le dossier, problème de réseau, etc.
Ex :
/**
* Téléverse l'image sélectionnée...
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\RedirectResponse
*/
public function store(ProduitRequest $request) : RedirectResponse
{
$reussi = false;
// *** génération du nom de fichier
// $uploadedFile sera de type illuminate\http\UplodadedFile
$uploadedFile = $request->file('photo'); // 'photo' est l'attribut name du <input type="file">
$nomFichierOriginal = pathinfo($uploadedFile->getClientOriginalName(), PATHINFO_FILENAME);
$nomFichier = stringToSlug($nomFichierOriginal) . '-' . uniqid();
$extension = $uploadedFile->extension();
// Le nom du dossier qui contiendra les images du produit est créé à partir du code du produit, qui est unique.
$slug = stringToSlug($request->code);
// *** enregistre le fichier sous public\medias\produits\$slug\xxx
try {
// $file sera de type Symfony\Component\HttpFoundation\File\File
// si on n'a pas besoin de la variable qui représente le fichier après l'avoir déplacé, on peut faire le move sans retenir $file
$file = $uploadedFile->move(public_path() . "/medias/produits/$slug", $nomFichier . '.' . $extension);
...
$reussi = true;
}
catch(\Symfony\Component\HttpFoundation\File\Exception\FileException $e) {
\Log::error("Erreur lors du téléversement du fichier. ", [$e]);
}
catch(\Throwable $e) {
\Log::error("Erreur inattendue. ", [$e]);
}
...
}
Remarquez que l'instruction :
$uploadedFile = $request->file('photo');
est l'équivalent de :
$uploadedFile = $request['photo'];
ou encore :
$uploadedFile = $request->photo;
Lorsque le téléversement est terminé, on peut, si la situation le demande, enregistrer les informations sur ce fichier dans la base de données.
Notez que lors de l'instantiation du modèle, il ne sera pas possible de retrouver le nom du fichier directement avec $request->all(). En effet, si on fait un dump and die de $request->all(), on obtiendra quelque chose du genre :
Produit {#201 ▼
...
#attributes: array:4 [▼
"categorie_id" => "1"
"code" => "ABC"
"description" => "une description"
"photo" => UploadedFile {#211 ▶}
]
...
}
On voit donc que la photo est un objet complexe. De plus, nous ne voulons pas enregistrer le nom original du fichier mais bien celui qui a été utilisé lors du téléversement.
Il faudra donc inscrire à la main la valeur à donner au champ photo après avoir récupéré le reste des informations du formulaire.
Dans le cas où les fichiers sont placés dans un sous-dossier, il serait prudent de stocker le nom du sous-dossier dans la BD et d'empêcher toute modification de cette information. En effet, si on utilise par exemple le code du produit pour nommer le dossier et que ce code change, il ne sera plus possible de retrouver les fichiers. Avec un slug non modifiable, on pourra éditer le code sans problème. Le slug demeurera disponible pour retrouver les fichiers.
Ex :
/**
* Téléverse l'image sélectionnée puis enregistre le nouveau produit.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\RedirectResponse
*/
public function store(ProduitRequest $request) : RedirectResponse
{
// téléversement de l'image
...
// enregistrement dans la BD
$produit = new Produit($request->all());
$produit->photo = $nomFichier . '.' . $extension;
$produit->slug = $slug;
$produit->save();
if ($reussi) {
flash('Le produit a été enregistré avec succès !', 'success');
}
else {
flash('Le produit a été enregistré avec succès mais un problème a empêché le téléversement de sa photo.', 'warning');
}
return redirect()->route('produits.index');
}
« Image upload and validation using Laravel and VueJs ». Jagadesha NH. https://medium.com/@jagadeshanh/image-upload-and-validation-using-laravel-and-vuejs-e71e0f094fbb
« Laravel 5.5 - multiple images uploading using dropzone js ». HD Tuto. https://hdtuto.com/article/laravel-55-multiple-images-uploading-using-dropzone-js
« How To Upload Multiple Files in Laravel 5.4 ». Laravel Daily. http://laraveldaily.com/upload-multiple-files-laravel-5-4/
« Type MIME ». Wikipédia. http://fr.wikipedia.org/wiki/Type_MIME
« List of MIME types / Internet Media Types ». FreeFormatter. https://www.freeformatter.com/mime-types-list.html
« Calling standard validation rules from a custom validation rule? ». Laracasts. https://laracasts.com/discuss/channels/general-discussion/calling-standard-validation-rules-from-a-custom-validation-rule
▼Publicité