TokenMismatchException in laravel 5.4 with image upload - php

I'm building a Laravel 5.4 application that let's you upload an image to each registered entry. I was using the intervention image package but realized I needed a way to enable image cropping and rotation (iphone images are rotated when uploaded for some reason), so I decided to use the jquery plugin Slim Cropper. I've added the necessary files to my code but can't succesfully upload an image.
Slim Cropper offers two ways to upload an image: through a regular form which gives me the "TokenMismatchException in VerifyCsrfToken.php (line 68)" after submitting, and an ajax form that simply shows a "cannot upload" message. I've tried both ways with different changes but can't get it to work. All my classes/controllers check for authentication, and I have tried sending the csrf token every way I could think of, all show the same error.
UPDATE: As per the suggestions in the comments, I've moved csrf token right after <form>, I've updated the input file names to match those from the example and attempted to debug through middleware with no error messages whatsoever. The TokenMismatchException error is no longer an issue, but once the form is submitted I get the error Constant expression contains invalid operations in Slim.php (line 106) for public static function saveFile($data, $name, $path = public_path('/uploads/mascotas-img/'), $uid = true). Still have no fix for this.
Here's the code:
Routes
Route::post('/mascotas/avatar', 'PetsController#avatar');
Pets Controller
use App\Slim;
public function avatar(Request $request)
{
if ( $request->avatar )
{
// Pass Slim's getImages the name of your file input, and since we only care about one image, postfix it with the first array key
$image = Slim::getImages('avatar')[0];
$mascota_num = $image['meta']->petId;
// Grab the ouput data (data modified after Slim has done its thing)
if ( isset($image['output']['data']) )
{
// Original file name
$name = $image['output']['name'];
//$name = $request->input('mascota_num');
// Base64 of the image
$data = $image['output']['data'];
// Server path
$path = public_path('/uploads/mascotas-img/');
// Save the file to the server
$file = Slim::saveFile($data, $name, $path);
// Get the absolute web path to the image
$imagePath = public_path('/uploads/mascotas-img/' . $file['name']);
DB::table('mascotas')
->where('num',$mascota_num)
->update(['foto' => $imagePath]);
//$mascota->foto = $imagePath;
//$mascota->save();
}
}
return redirect()->back()->with('success', "User's profile picture has been updated!");
}
Slim Class
namespace App;
abstract class SlimStatus {
const Failure = 'failure';
const Success = 'success';
}
class Slim {
public static function getImages($inputName = 'slim') {
$values = Slim::getPostData($inputName);
// test for errors
if ($values === false) {
return false;
}
// determine if contains multiple input values, if is singular, put in array
$data = array();
if (!is_array($values)) {
$values = array($values);
}
// handle all posted fields
foreach ($values as $value) {
$inputValue = Slim::parseInput($value);
if ($inputValue) {
array_push($data, $inputValue);
}
}
// return the data collected from the fields
return $data;
}
// $value should be in JSON format
private static function parseInput($value) {
// if no json received, exit, don't handle empty input values.
if (empty($value)) {return null;}
// The data is posted as a JSON String so to be used it needs to be deserialized first
$data = json_decode($value);
// shortcut
$input = null;
$actions = null;
$output = null;
$meta = null;
if (isset ($data->input)) {
$inputData = isset($data->input->image) ? Slim::getBase64Data($data->input->image) : null;
$input = array(
'data' => $inputData,
'name' => $data->input->name,
'type' => $data->input->type,
'size' => $data->input->size,
'width' => $data->input->width,
'height' => $data->input->height,
);
}
if (isset($data->output)) {
$outputData = isset($data->output->image) ? Slim::getBase64Data($data->output->image) : null;
$output = array(
'data' => $outputData,
'width' => $data->output->width,
'height' => $data->output->height
);
}
if (isset($data->actions)) {
$actions = array(
'crop' => $data->actions->crop ? array(
'x' => $data->actions->crop->x,
'y' => $data->actions->crop->y,
'width' => $data->actions->crop->width,
'height' => $data->actions->crop->height,
'type' => $data->actions->crop->type
) : null,
'size' => $data->actions->size ? array(
'width' => $data->actions->size->width,
'height' => $data->actions->size->height
) : null
);
}
if (isset($data->meta)) {
$meta = $data->meta;
}
// We've sanitized the base64data and will now return the clean file object
return array(
'input' => $input,
'output' => $output,
'actions' => $actions,
'meta' => $meta
);
}
// $path should have trailing slash
public static function saveFile($data, $name, $path = public_path('/uploads/mascotas-img/'), $uid = true) {
// Add trailing slash if omitted
if (substr($path, -1) !== '/') {
$path .= '/';
}
// Test if directory already exists
if(!is_dir($path)){
mkdir($path, 0755);
}
// Let's put a unique id in front of the filename so we don't accidentally overwrite older files
if ($uid) {
$name = uniqid() . '_' . $name;
}
$path = $path . $name;
// store the file
Slim::save($data, $path);
// return the files new name and location
return array(
'name' => $name,
'path' => $path
);
}
public static function outputJSON($status, $fileName = null, $filePath = null) {
header('Content-Type: application/json');
if ($status !== SlimStatus::Success) {
echo json_encode(array('status' => $status));
return;
}
echo json_encode(
array(
'status' => $status,
'name' => $fileName,
'path' => $filePath
)
);
}
/**
* Gets the posted data from the POST or FILES object. If was using Slim to upload it will be in POST (as posted with hidden field) if not enhanced with Slim it'll be in FILES.
* #param $inputName
* #return array|bool
*/
private static function getPostData($inputName) {
$values = array();
if (isset($_POST[$inputName])) {
$values = $_POST[$inputName];
}
else if (isset($_FILES[$inputName])) {
// Slim was not used to upload this file
return false;
}
return $values;
}
/**
* Saves the data to a given location
* #param $data
* #param $path
*/
private static function save($data, $path) {
file_put_contents($path, $data);
}
/**
* Strips the "data:image..." part of the base64 data string so PHP can save the string as a file
* #param $data
* #return string
*/
private static function getBase64Data($data) {
return base64_decode(preg_replace('#^data:image/\w+;base64,#i', '', $data));
}
}
Picture submit form (tokenmismatch error)
<form action="{{ url('mascotas/avatar') }}" method="post" enctype="multipart/form-data">
<div class="modal-body">
<div class="slim" data-label="Agregar imagen aquí" data-size="400, 400" data-ratio="1:1" data-meta-pet-id="{{ $mascota->num }}">
#if ( $mascota->foto )
<img src="{{ url('/uploads/mascotas-img/'.$mascota->foto) }}" />
#endif
<input type="file" name="avatar" required />
{{ csrf_field() }}
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
<button type="submit" class="btn btn-red">Cambiar Foto</button>
</div>
</form>
Alternate submit form (error message)
<div class="modal-body">
<div class="slim" data-label="Agregar imagen aquí" data-size="400, 400" data-ratio="1:1" data-service="{{ url('mascotas/avatar') }}" data-meta-pet-id="{{ $mascota->num }}">
#if ( $mascota->foto )
<img src="{{ url('/uploads/mascotas-img/'.$mascota->foto) }}" />
#endif
<input type="file" name="avatar" />
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
<button type="submit" class="btn btn-red">Cambiar Foto</button>
</div>
Slim image cropper website with examples http://slimimagecropper.com/
My original upload form through laravel image intervetion, this works with no problems at upload, but would very much like to replace with one of the above.
<form enctype="multipart/form-data" action="{{ url('mascotas/foto') }}" method="POST">
<div class="modal-body">
<img class="mascota-avatar" src="{{ url('/uploads/mascotas-img/'.$mascota->foto) }}">
<div class="clearfix"></div>
<input type="file" name="foto">
<input type="hidden" name="_token" value="{{ csrf_token() }}">
<input type="hidden" name="mascota_num" value="{{ $mascota->num }}">
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
<button type="submit" class="btn btn-red">Cambiar Foto</button>
</div>
</form>
Thanks for any help!

You should include the {{ csrf_field() }} on each of your forms, for the Ajax one, you could send the token as header.

Related

Fetch image variable and display on input file

I am trying to update an image and other data in a database, but when I update only text data, the image value becomes null or empty.
<form action="/admin/settings/why-us/update/{{$data->id}}" enctype="multipart/form-data" method="POST">
#csrf
<input type="text" class="form-control" name="title" value="{{$data->title}}">
<input type="file" class="form-control" value="{{$data->image}}" name="image">
<button type="submit" class="btn btn-success py-2 px-4 text-white">Update changes</button>
</form>
This a controller
public function updateWhyusPageSetting(Request $request,$id)
{
$title = $request->input('title');
$image = $image = $request->file('image');
dd($image);
if($request->hasFile('image')) {
$image = $request->file('image');
$filename = $image->getClientOriginalName();
$image->move(public_path('/frontend/images/'), $filename);
$image_upload = $request->file('image')->getClientOriginalName();
}
DB::table('features')
->where('id', $id)
->update([
'title' => $title,
'image' => $image_upload
]);
\Session::flash('flash_message', __('Why us data updated'));
\Session::flash('flash_type', 'success');
return redirect()->back();
}
When I input only the title, left out the image, and tried to dump using dd($image);, I got a null value.
When updating the image, it's getting updated very well database.
Now, my question is, how do I make sure the value is captured in the input file <input type="file" class="form-control" value="{{$data->image}}" name="image"> so that when I update other data, it also sends the image value. NB: value="{{$data->image}}" IS NOT capturing the data from database
Try this code
public function updateWhyusPageSetting(Request $request,$id){
$data = [];
$data['title'] = $request->input('title');
if($request->hasFile('image')) {
$image = $request->file('image');
$image->move(public_path('/frontend/images/'),$imageName = $image->hashName()); //hashName() will generate image name with extension
$data['image'] = $imageName; // here if user uploads an image, it will add to data array then add to DB.
}
DB::table('features')
->where('id', $id)
->update($data); // if a user uploaded an image will add. if not, a previous image will not change
\Session::flash('flash_message', __('Why us data updated'));
\Session::flash('flash_type', 'success');
return redirect()->back();
}
Please note you should delete the old images if you don't need anymore
you can use this to delete an old image if you want
(new Filesystem())->delete("full/path/with/image/name.jpg");

How to associate files to post with Laravel 5.6

I am using Laravel for my web app and I want to associate files to my posts in indepent way with his own form, but I have some problems
My routes (I am using a auth control package, but actually I am admin):
Route::post('file', 'fileController#store')->name('file.store')
->middleware('permission:file.create');
Route::get('file', 'fileController#index')->name('file.index')
->middleware('permission:file.index');
Route::get('file/create/', 'fileController#create')->name('file.create')
->middleware('permission:file.create');
Route::put('file/{id}', 'fileController#update')->name('file.update')
->middleware('permission:file.edit');
Route::get('file/{id}', 'fileController#show')->name('file.show')
->middleware('permission:file.show');
Route::delete('file/{id}', 'fileController#destroy')->name('file.destroy')
->middleware('permission:file.destroy');
Route::get('file/{id}/edit', 'fileController#edit')->name('file.edit')
->middleware('permission:file.edit');
Route::get('download/{filename}', 'fileController#download')->name('file.download')
->middleware('permission:file.download');
My migration:
Schema::create('files', function (Blueprint $table) {
$table->increments('id');
$table->integer('user_id')->unsigned();
$table->integer('files_id')->unsigned();
$table->string('filenames');
$table->integer('fileable_id')->unsigned();
$table->string('fileable_type');
$table->timestamps();
});
My File Model:
class File extends Model
{
protected $fillable = [
'filenames', 'project_id'
];
public function user()
{
return $this->belongsTo(User::class);
}
My Project Model:
public function files()
{
return $this->morphMany(File::class, 'fileable')->whereNull('files_id');
}
My Controller to store:
class FileController extends Controller
{
public function store(Request $request)
{
$this->validate($request, [
'filenames' => 'required',
'project_id' => 'required',
// 'filenames.*' => 'mimes:doc,pdf,docx,zip'
]);
if($request->hasfile('filenames'))
{
foreach($request->file('filenames') as $file)
{
$name=$file->getClientOriginalName();
$file->move(public_path().'/files/', $name);
$data[] = $name;
}
}
$file= new File();
$file->filenames = $request->get('filenames');
$file->filenames= $name;
$file->user()->associate($request->user());
$project = Project::findOrFail($request->get('project_id'));
$project->files()->save($file);
$file->save();
return back();
}
public function download( $filename = '' ) {
// Check if file exists in storage directory
$file_path = public_path() . '/files/' . $filename;
if ( file_exists( $file_path ) ) {
// Send Download
return \Response::download( $file_path, $filename );
} else {
return back()->with('info', 'Archivo no existe en el servidor');
}
}
The Form in blade:
<form method="post" action="{{ route('file.store') }}" enctype="multipart/form-data">
<div class="input-group hdtuto control-group lst increment" >
<input type="file" name="filenames[]" class="myfrm form-control">
<input type="hidden" name="project_id" value="{{ $project->id }}" />
<div class="input-group-btn">
<button class="btn btn-success" type="button"><i class="fldemo glyphicon glyphicon-plus"></i>Add</button>
</div>
</div>
<button type="submit" class="btn btn-success" style="margin-top:10px">Submit</button>
</form>
Foreach to download files:
#foreach($project->files as $file)
<li>{{ $file->user->name }}: <a href="{{ url('/download/')}}/{{$file->filenames}}" download> {{$file->filenames}}</a></li>
#endforeach
I send files from Project Controll
The reason you are getting the first error message is because the Project with the id you get from Request is not found in the Database and returns null instead of an object. That would mean you are indeed calling files() method on null. To resolve this there are multiple steps.
1.) Make sure project_id is inside the Request at all times:
$this->validate($request, [
'filenames' => 'required',
'project_id' => 'required',
// 'filenames.*' => 'mimes:doc,pdf,docx,zip'
]);
2.) Make sure to check for project if it exists after retrieving it from database, this can be done in two ways.
a) You can either find the project or throw an Exception if it's not found:
$project = Project::findOrFail($request->get('project_id');`
b) You can check with a simple if statement if it does not exist and do something
$project = Project::find($request->get('project_id');
if (!$project) {
// Project not found in database
// Handle it
}

Laravel - Request Validation to Image set as Nullable is not allowed and gives error why?

I need to validate my image array as an image and specific image file extensions only. but my request validation to image WONT ALLOW me to use inser nullable values
For example I will add a content and dont want to add images. then the image should contain null that is why i need to have request validation as nullable. But in my experience null value is not allowed and it gives me error why? help me please
here is the error.
Undefined variable: promotion
here is my CONTROLLER
public function store(Request $request)
{
$this->validate($request, [
'promotion_image' => 'image|nullable|max:1999'
]);
if ($request->has('promotion_image'))
{
//Handle File Upload
$promotion = [];
foreach ($request->file('promotion_image') as $key => $file)
{
// Get FileName
$filenameWithExt = $file->getClientOriginalName();
//Get just filename
$filename = pathinfo( $filenameWithExt, PATHINFO_FILENAME);
//Get just extension
$extension = $file->getClientOriginalExtension();
//Filename to Store
$fileNameToStore = $filename.'_'.time().'.'.$extension;
//Upload Image
$path = $file->storeAs('public/promotion_images',$fileNameToStore);
array_push($promotion, $fileNameToStore);
}
$fileNameToStore = serialize($promotion);
}
else
{
$fileNameToStore='noimage.jpg';
}
if (count($promotion)) {
$implodedPromotion = implode(' , ', $promotion);
$promotionImage = new Promotion;
$promotionImage->promotion_image = $implodedPromotion;
$promotionImage->save();
return redirect('/admin/airlineplus/promotions')->with('success', 'Image Inserted');
}
return redirect('/admin/airlineplus/promotions')->with('error', 'Something went wrong.');
}
here is my VIEW
{!! Form::open(['action'=>'Admin\PromotionsController#store', 'method' => 'POST','enctype'=>'multipart/form-data', 'name' => 'add_name', 'id' => 'add_name']) !!}
<div class="form-group">
<div class="table-responsive">
<table class="table table-bordered" id="dynamic_field">
<tr>
<td> {{ Form::file('promotion_image[]')}}</td>
<td>{{ Form::button('', ['class' => 'btn btn-success fa fa-plus-circle', 'id'=>'add','name'=>'add', 'style'=>'font-size:15px;']) }}</td>
</tr>
</table>
{{Form::submit('submit', ['class'=>'btn btn-primary', 'name'=>'submit'])}}
</div>
</div>
{!! Form::close() !!}
You need to declare $promotion = [] above if ($request->has('promotion_image')), not inside of it.
So:
public function store(Request $request)
{
$this->validate($request, [
'promotion_image' => 'image|nullable|max:1999'
]);
$promotion = [];
if ($request->has('promotion_image'))
{
//Handle File Upload
That is because your selecting file other than image in your form. See the following to restrict user to only upload images.
<input accept=".png, .jpg, jpeg" name="files[]" type="file" multiple>
Not sure but try once
'promotion_image' => 'nullable|mimes:jpeg,jpg,png,gif|max:1999'

Call to a member function getClientOriginalExtension() on a non-object error

I am trying to edit/update my image upload but I am getting "Call to a member function getClientOriginalExtension() on a non-object" error. please help
My controller:
public function update(Request $request, $id)
{
$lnkupdate=Request::all();
$links=Links::findorFail($id);
$file = Input::file('image');
$random_name = str_random(8);
$destinationPath = 'albums/';
$extension = $file->getClientOriginalExtension();
$filename=$random_name.'_link_logo.'.$extension;
$uploadSuccess = Input::file('image')->move($destinationPath, $filename);
ConsularGen::update(array(
'name'=>Input::get('name'),
'link' => Input::get('link'),
'image' => $filename,
));
}
View:
{!!Form::model($links,['method'=>'PATCH','action'=>['LinksController#update',$links->id]])!!}
<input type="hidden" name="_token" value="{{ csrf_token() }}">
<div class="form-group">
<label for="image">Select a logo</label>
{!!Form::file('image')!!}
</div>
<div class="form-goup">
{!!Form::label('name','Name')!!}
{!!Form::text('name',null,['class'=>'form-control'])!!}
</div>
<div class="form-goup">
{!!Form::label('link','Link')!!}
{!!Form::text('link',null,['class'=>'form-control'])!!}
</div>
<div class="form-group">
<button type="submit" class="btnbtn-default">Add</button>
</div>
{!!Form::close()!!}
Route:
Route::patch('admin/links/{id}/update','LinksController#update');
Uploading files requires the html form to specify enctype="multipart/form-data". If you don't have this, the file will not be uploaded, Input::file('image') will return null, and you'll get the error you're seeing.
The Laravel Form builder will add this to your form if you tell it that it needs to handle files. Add 'files' => true to your array in the form:
{!! Form::model($links, ['method'=>'PATCH', 'files' => true, 'action'=>['LinksController#update', $links->id]]) !!}
Once this is fixed, you'll also get this error if you don't actually select a file to be uploaded. You should wrap your file handing inside a check to hasFile. Something like:
public function update(Request $request, $id)
{
$lnkupdate=Request::all();
if (Input::hasFile('image')) {
$links=Links::findorFail($id);
$file = Input::file('image');
$random_name = str_random(8);
$destinationPath = 'albums/';
$extension = $file->getClientOriginalExtension();
$filename=$random_name.'_link_logo.'.$extension;
$uploadSuccess = Input::file('image')->move($destinationPath, $filename);
ConsularGen::update(array(
'name'=>Input::get('name'),
'link' => Input::get('link'),
'image' => $filename,
));
} else {
echo 'no file uploaded. oops.';
}
}
Your file has not uploaded successfully.
you are trying to run getClientOriginalExtension() on a null file thats's why you are getting this error

File Upload Redirect with Error Not Display

I'm not sure what is the cause of my file upload. It's failing and redirecting back, but not with error.
Controller
public function updateLogo()
{
$inputs = Input::all();
$logo_path = Input::only('logo_path');
$cpe_mac = $inputs['cpe_mac'];
$rule = ['logo_path' => 'mimes:jpeg,bmp,png'];
$validator = Validator::make($logo_path, $rule );
if ( $validator->fails()) {
return Redirect::to($cpe_mac.'/view-profile/')->withErrors($validator)->withInput();
} else {
.....
}
View
{!! Form::open(array('url' => '/'.$cpe_mac.'/view-profile/logo/update', 'class' => 'form-horizontal', 'role' =>'form','id' => 'editLogo','file'=>true)) !!}
<input name="logo_path" type="file" required> <br><br>
<button class="btn btn-success btn-sm mr5" type="file"><i class="fa fa-user"></i> Update Logo</button>
{!! Form::hidden('cpe_mac', $cpe_mac)!!}
{{ csrf_field() }}
{!! Form::close();!!}
Did I forget something ?
A file (really uploaded file, so not just a string) can be called with Input::file(); It isn't included in the standard Input::all().
You need to change your validation slightly in order to get this to work to follow up on #marmorunl answer.
public function updateLogo()
{
$input = [
'logo_path' => Input::file('logo_path')
];
$rules = [
'logo_path' => 'mimes:jpeg,bmp,png|required'
];
$validator = Validator::make($input, $rules);
if ($validator->fails()) {
return Redirect::to($cpe_mac.'/view-profile/')->withErrors($validator)->withInput()
} else {
// else
}
};
See Laravel: Validate an uploaded file is an image for additional information. This suggests using image as the validation rule which would also include git and svg. So I've left it as the mime type one you where using in case you don't want this.

Categories