I am giving the validation rule for image file types is:
array('employeedetails_photo', 'file', 'types' => 'jpg,gif,png', 'allowEmpty' => true,'on' => 'insert', 'on' => 'update'),
But, the problem is that if extension of .doc file is changed to .docx.jpg and upload this as employee photo is also accepted. How to give validation for this issue in yii ?
The best way to validate pictures is to recreate them using GD imagecreatefrom* (or Imagick) and save/replace the processed image. The most simple example
public function rules(){
return array(
array( 'employeedetails_photo', 'validateImage' )
);
}
public function validateImage( $attribute ){
$file = CUploadedFile::getInstance($this, $attribute);
if ( !$file ) {
return;
}
// http://php.net/manual/en/function.imagecreatefromstring.php
// These types will be automatically detected if your build of PHP supports them: JPEG, PNG, GIF, WBMP, and GD2
$gd = #imagecreatefromstring(file_get_contents($file->getTempName()));
if ($gd === false) {
$this->addError($attribute, 'Image is corrupted');
}
}
You must check file type with file mimetype. Php should made it for you. use mime-content-type function
Related
Laravel version: 8.82.0
I am uploading an image with inertiajs by making a post request with form.post method. An image data is received in controller as depicted in an image below:
My simplified controller:
public function store(){
Request()->validate([
'thumbnail' => 'required|mimes:png,jpeg,jpg|max:2048'
// Also tried, still fails.
// 'thumbnail' => 'required|image|mimes:png,jpeg,jpg|max:2048'
]);
return Redirect::route('showcases.index');
}
Upon ariving header content-type is application/json, I tried changing it to multipart/form-data but to no avail, image validation still fails.
The error I get is: The thumbnail must be a file of type: png, jpeg, jpg
you are trying to validate a base64 string instead of a file.
in the string format laravel validation cant validate the mime type.
so you may try to extend the validation rules and try a better way.
Inside the AppServiceProvider put the custom validation(there are many other cleaner ways)
public function boot()
{
Validator::extend('image64', function ($attribute, $value, $parameters, $validator) {
$type = explode('/', explode(':', substr($value, 0, strpos($value, ';')))[1])[1];
if (in_array($type, $parameters)) {
return true;
}
return false;
});
Validator::replacer('image64', function($message, $attribute, $rule, $parameters) {
return str_replace(':values',join(",",$parameters),$message);
});
}
now you can do :
'thumbnail' => 'required|image64:jpeg,jpg,png'
I'm trying to create images/user_avatars with Laravel Nova admin panel and with api. I need generate 3 pictures for each download - avatar, thumbnail and preview.
public function fields(Request $request)
{
return [
//some fields else
Avatar::make(__('Profile Photo'), 'avatar_path')
->path('images/users/avatars')
->storeAs(function (Request $request){
$file = $request->file('avatar_path');
return $request->user()->id.'_'. sha1($file->getClientOriginalName()).'.'.$file->getClientOriginalExtension();
})
->preview(function ($value, $disk){
return $this->getCroppedAvatar($value, 'prev', 636);
})
->thumbnail(function ($value, $disk){
return $this->getCroppedAvatar($value, 'thumb', 64);
})
->disableDownload(),
In this field i'm using the following method
public static function getCroppedAvatar($value, $type, $size)
{
$path = str_replace('avatars', $type , $value);
if ($value && !Storage::exists($value)) {
return null;
}
if ($value && is_file(Storage::path($path)) && !is_dir(Storage::path($path))) {
return Storage::url($path);
}
if ($value) {
Image::make(Storage::path($value))
->widen($size)->save(Storage::path($path));
Log::info('New preview is ' . Storage::path($path));
return Storage::url($path);
}
return null;
}
In this case Laravel Nova is working, but if I try to call getCroppedAvatar from API Controller, i have an error on Image::make line:
Unsupported image type. GD driver is only able to decode JPG, PNG, GIF or WebP files. {"userId":16,"exception":"[object] (Intervention\\Image\\Exception\\NotReadableException(code: 0): Unsupported image type. GD driver is only able to decode JPG, PNG, GIF or WebP files. at /var/www/tracker/vendor/intervention/image/src/Intervention/Image/Gd/Decoder.php:59)
Code on API Controller
Storage::put($fileDirectory.'/'.$fileName, $image);
User::getCroppedAvatar($fileDirectory.'/'.$fileName,'prev', 636);
User::getCroppedAvatar($fileDirectory.'/'.$fileName,'thumb', 64);
In both cases i'm using png and jpg files. Argument values are the same.
How can i fix it?
Thank you!
Finally, i found solution. As i understand, Storage class creating not correct type of image, so i changed this line from API Controller
Storage::put($fileDirectory.'/'.$fileName, $image);
for this:
Image::make($file)->save(Storage::path($filePath));
I am working on a Laravel application and at some point, when I allow users to upload their own images for different purposes, I want to generate resized previews for these images.
I am uploading all user content to Amazon S3 and the first thing I did about resizing image is uploading the original image, then went through a foreach, resized the image and re-uploaded it to S3.
As you can image, having 4 sizes for each image dramatically increases the upload time and is a performance concern for me.
Here is a code snippet that I use in my upload function:
$storageDriver = Storage::disk('cloud-storage')->getDriver();
$parentSuccess = $storageDriver->put("/$parentId", file_get_contents($file), [
'visibility' => 'public',
'ACL' => 'public-read',
'ContentType' => $contentType,
]);
$ratio = $imageSize[0] / $imageSize[1];
foreach (self::IMAGE_SIZES as $size) {
if ($size > $imageSize[0] || ($size / $ratio) > $imageSize[1]) {
continue;
}
$id = DB::table('storage')->insertGetId([
'content_type' => $contentType,
'parent_id' => $parentId,
'image_width' => $size,
'image_height' => intval($size / $ratio),
]);
$image = Image::make($file)->encode('jpg');
$image->resize($size, null, function ($constraint) {
$constraint->aspectRatio();
$constraint->upsize();
});
$image->save();
$success = $storageDriver->put("/$id", (string)$image, [
'visibility' => 'public',
'ACL' => 'public-read',
'ContentType' => 'image/jpeg',
]);
if (!$success) {
return null;
}
}
(I know there is a lot of code not included, but it's not relevant).
What method would you choose for handling this in a more efficient way?
Had to implement something like this in my last project, I used Lazy loading. Upload the parent image to s3 and generate the thumbnails only when needed.
You can have the function getThumbnails() attached to the model that has image. The function checks if thumbnails have been generated for that model and returns it, else it generates them.
public function getThumbnails(){
$thumbDir = "path/to/model/thumbnail/dir";
//Check if the folder exists.
//You can also double check if the directory actually has the thumbnails in it
if(!file_exists($thumbDir)){
$this->generateThumbnails($thumbDir);
}
//Return something
}
For more fun, you can be more specific and have a function handle each thumbnail.
public function getThumbnailX(){
$thumb = "path/to/specific/model/thumbnail";
if(!file_exists($thumb)){
$this->generateThumbnailX($thumb);
}
return $thumb;
}
public function getThumbnailX2(){
...
}
Super fun, add it as an attribute of the model.
public function getThumbXAttribute(){
return getThumbnailX();
}
so you can go ahead and call $model->thumbX whenever you need thumbX.
This reduces processing time and also the size of storage required as not all images may have their thumbnail generated.
I am building a form which uploads multiple files ,its working fine but the problem is my validation message ,my validation is checking the mime type of the file .
suppose the uploaded file with name filename
The validation message :
The file.0 must be a file of type: pdf, doc, docx, jpeg, jpg, png, bnp.
i want to change it to
The filename must be a file of type: pdf, doc, docx, jpeg, jpg, png, bnp.
My form :
{!! Form::open(['route' => ['file.store'], 'method'=> 'POST', 'id' => 'file_form','files' => 'true']) !!}
{!!Form::FILE('file[]', ['id' => 'file','multiple' =>true])!!}
<button type="submit" name="submit">save</button>
{!! Form::close() !!}
my formRequest validation
foreach ($this->file as $key => $val)
{
$rules['file.' . $key] = 'mimes:pdf,doc,docx,jpeg,jpg,png,bnp|extension|max:10240'
}
return rules;
Hi if u are using the FormRequest to validate.
Thats how i achieved it.
Here is an example.
public function messages()
{
$messages = [];
foreach($this->request->get('items') as $key => $val)
{
$messages['items.'.$key.'.max'] = 'The field labeled "Book Title '.$key.'" must be less than :max characters.';
}
return $messages;
}
Use custom validation message
Add this line in custom array in validation.php inside resource/lang/en/ folder
'custom' => [
.........
'file.*' => [
'mimes' => 'The filename must be a file of type: :values.',
],
I recommend, rename file to unique_name to not conflict with name
I appropriate all of you for your answers,i have found another way that fits to my project. I have created custom validation called custom_mimes to validate file type based on mime type ,and adding custom replacer (:filename) to this validation.
\Validator::extend('custom_mimes', function ($attribute, $file, $parameters, $validator)
{
$validator->addReplacer('custom_mimes', function ($message, $attribute, $rule, $parameters) use ($file)
{
$values=implode(',',$parameters);
return str_replace([':filename',':values'], [$file->getClientOriginalName(),$values], $message);
});
$mime_type =$file->guessExtension();
return in_array($mime_type,$parameters);
});
and in validation.php
'custom_mimes' => 'The :filename must be a file of type: :values.',
so my key and values will be :
:filename => uploaded filename
:values =>'jpg,png,doc,docx'
Inside an Form Request you can simply use it like below:
public function messages()
{
$messages = [];
foreach($this->request->get('files') as $key => $val)
{
$messages['files.'.$key. ". max"] = 'The file field cannot be greater then 10M';
}
return $messages;
}
I want to update a record in my model, and it contains a file field, here is the form code:
<div class="row">
<?php echo $form->labelEx($model,'img'); ?>
<?php echo CHtml::activeFileField($model,'img',array('width'=>25,'lentgh'=>25)); ?>
<?php echo $form->error($model,'img'); ?>
</div>
and here is the update action in the controller:
$model=$this->loadModel($id);
$img = $model->img;
if(isset($_POST['Press']))
{
$model->attributes=$_POST['Press'];
$model->img = $img;
if(isset($_POST['Press']['img'])){
if(!empty ($_POST['Press']['img']))
$model->img = CUploadedFile::getInstance($model, 'img');
So if the user didn't upload an image, the value of img attribute should not be updated and the model should be validated, but I got validation error every time I click on save and the img file filed is empty, so how I can fix this issue ?
Form validations are handeled by the model.
You can set an imagefield of filefield to be allowed to be empty like this in your model.php:
array('image', 'file', 'types'=>'jpg,png,gif',
'maxSize'=>1024 * 1024 * 5,
'allowEmpty' => true),
EDIT:
You can check if the file is empty before overwriting the current value of the object
$imageUploadFile = CUploadedFile::getInstance($model, 'image');
if($imageUploadFile !== null){ // only do if file is really uploaded
$imageFileName = mktime().$imageUploadFile->name;
$model->image = $imageFileName;
}
You can use csenario as:
array('image', 'file', 'types'=>'jpg,png,gif',
'maxSize'=>1024 * 1024 * 5,
'allowEmpty' => false, 'on'=>'insert'),
array('image', 'file', 'types'=>'jpg,png,gif',
'maxSize'=>1024 * 1024 * 5,
'allowEmpty' => true, 'on'=>'update'),
after that it will allow empty field on update
Faced the same issue and here is my solution.
But first I will describe how I work with models.
Basic POST processing
if (Yii::app()->request->getIsPostRequest()) {
$basicForm->attributes = Yii::app()->request->getParam(get_class($basicForm));
if ($basicForm->validate()) {
$logo = CUploadedFile::getInstance($basicForm, "logo");
if (!is_null($logo)) {
try {
// uploads company logo to S3
} catch (\Exception $e) {
// display any kind of error to the user or log the exception
}
}
if ($basicForm->save(false))
$this->refresh();
}
}
and my logo field has rule ["logo", "file", "mimeTypes" => ["image/png", "image/jpeg"], "allowEmpty" => true].
This rule gives me freedom to upload or not to upload the file, BUT if I want to change another form field not changing the file it will empty the model's logo field and my database too.
Problem
File gets empty if form update trying to update another form field, not file
Why it happening?
This happening because file validator expects CUploadedFile type of object when you have string in the model's logo field. String from database where you storing path to logo. And string is not CUploadedFile. And after if ($basicForm->validate()) model resets the logo field to null.
Solution
Own validation rule which will upload/reupload the file if logo is of hype `` and do nothing if logo of basic string type. Here I will put a basic "in-model" validator, it is up to you to move it into separate class, etc.
public function checkFile($attribute, $params)
{
$mimeTypes = isset($params["mimeTypes"]) ?$params["mimeTypes"] :[];
$allowEmpty = isset($params["allowEmpty"]) ?$params["allowEmpty"] :false;
/** #var CUploadedFile $value */
$value = $this->{$attribute};
if (!$allowEmpty && empty($value))
$this->addError($attribute, "{$attribute} can not be empty");
if (!empty($value)) {
if (is_object($value) && CUploadedFile::class === get_class($value) && 0 < sizeof($mimeTypes)) {
if (!is_array($value->type, $mimeTypes))
$this->addError($attribute, "{$attribute} file is of wrong type");
} elseif (!is_string($value)) {
// we can die silently cause this error won't actually ever get to the user in normal use cases
$this->addError($attribute, "{$attribute} must be of type either CUploadedFile or PHP string");
}
}
}
I called it checkFile and then the logo rule becomes ["logo", "checkFile", "mimeTypes" => ["image/png", "image/jpeg"], "allowEmpty" => true],
That is it. Enjoy.
Note: I'm putting this here as an example. Code may not be completely correct, or used as is, all the stuff like that... :)
I'had same problem. In my user model there wos a field containing path to image and i wanted to update model and fileField cleared my images.
So i figured workaround - store field containing path_to_file at start of action "update" in $tmp_variable and if there is no new upload - just set it before save():
public function actionUpdate($id)
{
$request = Yii::app()->request;
$model = $this->loadModel($id, 'YourModel');
$tmpPathToImage = $model->your_path_to_file_in_model;
$yourModelPost = $request->getPost('YourModel');
if (!empty($yourModelPost)) {
$model->setAttributes($yourModelPost);
$fileSource = Yii::getPathOfAlias('webroot.uploads.yourModel');
$imgTmp = CUploadedFile::getInstance($model, 'your_path_to_file_in_model');
if ($imgTmp !== null) {
$imgTmp->saveAs($fileSource.'/'.$imgTmp->name);
$model->your_path_to_file_in_model = '/'.UPLOADS_YOUR_MODEL_FILE_PATH_RELATIVE.'/'.$imgTmp->name;
}
else {
$model->your_path_to_file_in_model = $tmpPathToImage;
}
if ($model->save()) {
$this->redirect(['/admin/yourModel/index']);
}
}
$this->render('update', [
'model' => $model,
]);
}
Hope it helps someone... :)