Im saving a request to my database from my vue js via;
public function store(Request $request)
{
//validate
$this->validate($request, [
'name' => 'required',
'description' => 'required',
'price' => 'required'
]);
//get image
$exploded = explode(',', $request->cover_image);
$decoded = base64_decode($exploded[1]);
if(str_contains($exploded[0],'jpeg'))
$extension = 'jpg';
else
$extension = 'png';
$fileName = str_random().'.'.$extension;
$path = public_path().'/cover_images/'.$fileName;
file_put_contents($path, $decoded);
//save
$product = new Product;
$product->name = $request->input('name');
$product->description = $request->input('description');
$product->price = $request->input('price');
$product->cover_image = $fileName;
if($product->save()) {
return new ProductsResource($product);
}
}
How can I validate the base64 image? Is my procedure in saving the image coming from vue js is in correct way or is there a better way? please let me know. Thanks im just new to laravel and vue js hoping to learn more
You should add this function to your custom helper :
if (!function_exists('validate_base64')) {
/**
* Validate a base64 content.
*
* #param string $base64data
* #param array $allowedMime example ['png', 'jpg', 'jpeg']
* #return bool
*/
function validate_base64($base64data, array $allowedMime)
{
// strip out data uri scheme information (see RFC 2397)
if (strpos($base64data, ';base64') !== false) {
list(, $base64data) = explode(';', $base64data);
list(, $base64data) = explode(',', $base64data);
}
// strict mode filters for non-base64 alphabet characters
if (base64_decode($base64data, true) === false) {
return false;
}
// decoding and then reeconding should not change the data
if (base64_encode(base64_decode($base64data)) !== $base64data) {
return false;
}
$binaryData = base64_decode($base64data);
// temporarily store the decoded data on the filesystem to be able to pass it to the fileAdder
$tmpFile = tempnam(sys_get_temp_dir(), 'medialibrary');
file_put_contents($tmpFile, $binaryData);
// guard Against Invalid MimeType
$allowedMime = array_flatten($allowedMime);
// no allowedMimeTypes, then any type would be ok
if (empty($allowedMime)) {
return true;
}
// Check the MimeTypes
$validation = Illuminate\Support\Facades\Validator::make(
['file' => new Illuminate\Http\File($tmpFile)],
['file' => 'mimes:' . implode(',', $allowedMime)]
);
return !$validation->fails();
}
}
Then extend the base64_image validation in your AppServiceProvider in boot() method :
use Illuminate\Support\Facades\Validator;
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap services.
*
* #return void
*/
public function boot()
{
...
Validator::extend('base64_image', function ($attribute, $value, $parameters, $validator) {
return validate_base64($value, ['png', 'jpg', 'jpeg', 'gif']);
});
}
Now you can use it in your validation rules like this :
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
'photo' => 'required|base64_image'
];
}
There is a crazybooot/base64-validation package that handles base64 validation.
For installation instructions and more details see:
https://github.com/crazybooot/base64-validation
Related
I am working on this Laravel 5.4 Social Network Script and when I post something the avatar is being displayed "animated" but when it is being saved it is converted to an static image.
I have this in the controller TimelineController#createPost:
...
use App\Media;
use App\Post;
use Intervention\Image\Facades\Image;
...
public function createPost(Request $request)
{
$input = $request->all();
$post = Post::create($input);
...
if ($request->file('post_images_upload_modified')) {
foreach ($request->file('post_images_upload_modified') as $postImage) {
$strippedName = str_replace(' ', '', $postImage->getClientOriginalName());
$photoName = date('Y-m-d-H-i-s').$strippedName;
$avatar = Image::make($postImage->getRealPath());
$avatar->save(storage_path().'/uploads/users/gallery/'.$photoName, 60);
$media = Media::create([
'title' => $photoName,
'type' => 'image',
'source' => $photoName,
]);
$post->images()->attach($media);
}
}
...
}
...
It uses GD Library to process the images internally.
The file Image.php on /vendor/intervention/image/src/Intervention/Image/:
#method \Intervention\Image\Image make(mixed $source) Universal factory method to create a new image instance from source, which can be a filepath, a GD image resource, an Imagick object or a binary image data.
/**
* Starts encoding of current image
*
* #param string $format
* #param integer $quality
* #return \Intervention\Image\Image
*/
public function encode($format = null, $quality = 90)
{
return $this->driver->encode($this, $format, $quality);
}
/**
* Saves encoded image in filesystem
*
* #param string $path
* #param integer $quality
* #return \Intervention\Image\Image
*/
public function save($path = null, $quality = null)
{
$path = is_null($path) ? $this->basePath() : $path;
if (is_null($path)) {
throw new Exception\NotWritableException(
"Can't write to undefined path."
);
}
$data = $this->encode(pathinfo($path, PATHINFO_EXTENSION), $quality);
$saved = #file_put_contents($path, $data);
if ($saved === false) {
throw new Exception\NotWritableException(
"Can't write image data to path ({$path})"
);
}
// set new file info
$this->setFileInfoFromPath($path);
return $this;
}
The stored image is a GIF but resized despite it is not specified:
Do you know how to make the image animated when saved?
I have 2 methods in my Controller and I need to validate it but I don't know how.
1st method which should allow all image extensions:
public function testing(Request $request) {
if($request->hasFile('img')) {
$image = Input::file('img');
$filename = time() . '.' . $image->getClientOriginalExtension();
$path = public_path('images/' . $filename);
Image::make($image->getRealPath())->resize(200, 200)->save($path);
$file = $request->file('img');
return ['url' => url('images/' . $filename)];
}
}
2nd method which should only allow 1 word and if there is space, trim it into 1 word:
public function postDB(Request $request) {
$newName = $request->input('newName');
$websites = new Website();
$websites->name = $newName;
$websites->save();
return redirect('template')->with('status', 'Website has been saved successfully!');
}
First write new Request for your data
php artisan make:request ImageRequest
Than write in ImageRequest:
public function authorize()
{
return true;
}
public function rules()
{
return [
'img' => 'file|image',
]
}
If you want to customize error messages:
public function messages()
{
return [
'img.image' => 'Some custom message ...',
];
}
Last inject request to your method (don`t forget about use App\Http\Requests):
public function testing(Requests\ImageRequest $request) {
//for retrieving validation errors use:
$imgErrors = $errors->first('img');
}
More information about Form Request Validation
Or you can use Validator facade (don`t forget about use Validator):
$validator = Validator::make(
$image, [
'img' => 'file|image',
]
);
More information about A Note On Optional Fields
I am using Ionic framework for mobile app development. The Yii2 API code below can be used for file upload, but it doesn't work. It shows the following errors:
i) Undefined offset: 0.
ii) yii\db\BaseActiveRecord->save()
public function actionNew() {
$model = new Apiprofile();
$userid = $_REQUEST['user_id'];
$photo = $_FILES['photo'];
$model->user_id = $userid;
$model->photo = $photo;
$name = $model->user_id;
$model->file = UploadedFile::getInstance($model, 'photo');
if($model->file) {
$model->file->saveAs('uploads/photos/'.$name.'.'.$model->file->extension);
$model->photo = $name.'.'.$model->file->extension;
$model->save();
}
$name = $model->user_id;
if($model->save()) {
echo json_encode(array('status'=>1,'data'=>$model->attributes),JSON_PRETTY_PRINT);
} else {
echo json_encode(array('status'=>0,'error_code'=>400,'errors'=>$model->errors),JSON_PRETTY_PRINT);
}
}
Hi if you want I will share my helper class that I used for working with images in Yii2 REST.
In the base folder of your application create folder components and inside of that folder create two folders helpers and objects.
-->assets
-->componests
----->helpers
----->objects
-->config
-->...
after that create class FileUpload inside of objects folder and put this code inside.
<?php
namespace app\components\objects;
class FileUpload
{
public $error=[];
public $isSuccess=false;
public $file;
public $ready_path;
public $file_type;
public $file_size;
public $file_extension;
public $file_tmp_name;
public $file_name;
public $save_path;
public function __construct($file,$save_path,$ready_path,$required=false)
{
if(!isset($_FILES[$file])){
if($required){
$this->error=['message'=>$file.' is required'];
}
return $this;
}
$this->save_path=$save_path;
$this->ready_path=$ready_path;
$this->file_type = strtolower($_FILES[$file]['type']);
$this->file_name = $_FILES[$file]['name'];
$this->file_tmp_name=$_FILES[$file]['tmp_name'];
$this->file_size = $_FILES[$file]['size'];
$this->file_extension=pathinfo($this->file_name, PATHINFO_EXTENSION);
}
public function setError($error){
if(empty($this->error)){
$this->error=$error;
}
}
}
Then inside of helpers folder create class FileUploadHelper and then put this code inside.
<?php
namespace app\components\helpers;
use app\components\objects\FileUpload;
use Yii;
class FileUploadHelper
{
private $allowed_files;
private $file_size;
const IMAGE='image';
const FILE='file';
/** File Upload
* #param string $file_name
* #param string $save_path
* #param string $ready_path
* #param string $type
* #param bool $required
* #return \app\components\objects\FileUpload
*/
public static function fileUpload($file_name,$save_path,$ready_path,$type,$required=false){
$image=new FileUpload($file_name,$save_path,$ready_path,$required);
if($type==self::FILE){
$allowed_files=Yii::$app->params['allowed_files'];
$file_size=Yii::$app->params['file_max_size'];
}else{
$allowed_files=Yii::$app->params['allowed_files'];
$file_size=Yii::$app->params['file_max_size'];
}
$dir = realpath(Yii::$app->basePath);
if(in_array($image->file_type,$allowed_files)
&&$image->file_size<$file_size){
$filename = $file_name.'_'.md5(uniqid(time()).time() . '_' . date('YmdHis')) . '.' . $image->file_extension;
$file = $dir . $image->save_path . $filename;
if(move_uploaded_file($image->file_tmp_name, $file)){
$image->file=$image->ready_path. $filename;
$image->isSuccess=true;
$image->setError(['message'=>'file_upload_success']);
}else{
$image->setError(['message'=>'error_try_again']);
}
}else{
$image->setError(['message'=>'file_should_be_no_more_than_given_size']);
}
return $image;
}
/** Delete File
* #param string $ready_file
*/
public static function deleteImage($ready_file){
$dir = realpath(Yii::$app->basePath);
if (strpos($ready_file, 'default') === false){
if(is_file($dir.'/web/'.$ready_file)){
unlink($dir.'/web/'.$ready_file);
}
}
}
}
That's all needed. Below I will give you example
public function actionEditAvatar($id)
{
$product = Product::findOne($id);
if( $product ){
$old_avatar=$product->avatar;
$image=FileUploadHelper::fileUpload(ProductForm::AVATAR,Yii::$app->params['product_path'],Yii::$app->params['product_ready_path'],FileUploadHelper::IMAGE);
if($image->isSuccess) {
$product->avatar = $image->file;
if($product->save()){
FileUploadHelper::deleteImage($old_avatar);
return $product->avatar;
}
}
return $image->error;
}
throw new NotFoundHttpException;
}
The code above from a real project. FileUploadHelper has two static classes which are "fileUpload" and "deleteImage".
FileUploadHelper requires fileUpload('file_name','save_path','ready_path','type')
'save_path' is where the file will be saved.
'ready_path' is how the ready URL should be like.
they are in Yii::$app->params[];
You can check if image succeeded or not by attribute isSuccess. If you have the error you can access them by attribute error. The ready file can be accessed by attribute file. You can delete image via static function deleteImage('saved_image_url'); All of them are used at above action. Please see it.
By the way, here the params that I used. Do not forget to to create folders inside web/uploads and change names of the folders like in config file.
return [
'adminEmail' => 'admin#example.com',
'allowed_images' => [ 'image/jpeg', 'image/gif', 'image/png' ],
'allowed_files' => [ 'application/vnd.openxmlformats-officedocument.wordprocessingml.document','application/msword', 'application/pdf','image/jpeg', 'image/gif', 'image/png'],
'image_max_size' => 2097152,
'file_max_size' => 8388608,
'owner_document_path' => '/web/uploads/owner_document/',
'owner_document_ready_path' => 'uploads/owner_document/',
'product_path' => '/web/uploads/product/',
'product_ready_path' => 'uploads/product/',
'complain_photo_path' => '/web/uploads/complain_photo/',
'complain_photo_ready_path' => 'uploads/complain_photo/',
'owner_path' => '/web/uploads/owner/',
'owner_ready_path' => 'uploads/owner/',
'staff_path' => '/web/uploads/staff/',
'staff_ready_path' => 'uploads/staff/',
];
Im trying to test an upload API but it fails every time:
Test Code :
$JSONResponse = $this->call('POST', '/upload', [], [], [
'photo' => new UploadedFile(base_path('public/uploads/test') . '/34610974.jpg', '34610974.jpg')
]);
$this->assertResponseOk();
$this->seeJsonStructure(['name']);
$response = json_decode($JSONResponse);
$this->assertTrue(file_exists(base_path('public/uploads') . '/' . $response['name']));
file path is /public/uploads/test/34610974.jpg
Here is My Upload code in a controller :
$this->validate($request, [
'photo' => 'bail|required|image|max:1024'
]);
$name = 'adummyname' . '.' . $request->file('photo')->getClientOriginalExtension();
$request->file('photo')->move('/uploads', $name);
return response()->json(['name' => $name]);
How should I test file upload in Laravel 5.2? How to use call method to upload a file?
When you create an instance of UploadedFile set the last parameter $test to true.
$file = new UploadedFile($path, $name, filesize($path), 'image/png', null, true);
^^^^
Here is a quick example of a working test. It expects that you have a stub test.png file in tests/stubs folder.
class UploadTest extends TestCase
{
public function test_upload_works()
{
$stub = __DIR__.'/stubs/test.png';
$name = str_random(8).'.png';
$path = sys_get_temp_dir().'/'.$name;
copy($stub, $path);
$file = new UploadedFile($path, $name, filesize($path), 'image/png', null, true);
$response = $this->call('POST', '/upload', [], [], ['photo' => $file], ['Accept' => 'application/json']);
$this->assertResponseOk();
$content = json_decode($response->getContent());
$this->assertObjectHasAttribute('name', $content);
$uploaded = 'uploads'.DIRECTORY_SEPARATOR.$content->name;
$this->assertFileExists(public_path($uploaded));
#unlink($uploaded);
}
}
➔ phpunit tests/UploadTest.php
PHPUnit 4.8.24 by Sebastian Bergmann and contributors.
.
Time: 2.97 seconds, Memory: 14.00Mb
OK (1 test, 3 assertions)
In Laravel 5.4 you can also use \Illuminate\Http\UploadedFile::fake(). A simple example below:
/**
* #test
*/
public function it_should_allow_to_upload_an_image_attachment()
{
$this->post(
action('AttachmentController#store'),
['file' => UploadedFile::fake()->image('file.png', 600, 600)]
);
/** #var \App\Attachment $attachment */
$this->assertNotNull($attachment = Attachment::query()->first());
$this->assertFileExists($attachment->path());
#unlink($attachment->path());
}
If you want to fake a different file type you can use
UploadedFile::fake()->create($name, $kilobytes = 0)
More information directly on Laravel Documentation.
I think this is the easiest way to do it
$file=UploadedFile::fake()->image('file.png', 600, 600)];
$this->post(route("user.store"),["file" =>$file));
$user= User::first();
//check file exists in the directory
Storage::disk("local")->assertExists($user->file);
and I think the best way to delete uploaded files in the test is by using tearDownAfterClass static method,
this will delete all uploaded files
use Illuminate\Filesystem\Filesystem;
public static function tearDownAfterClass():void{
$file=new Filesystem;
$file->cleanDirectory("storage/app/public/images");
}
The laravel documentation has an answer for when you want to test a fake file. When you want to test using a real file in laravel 6 you can do the following:
namespace Tests\Feature;
use Illuminate\Http\UploadedFile;
use Tests\TestCase;
class UploadsTest extends TestCase
{
// This authenticates a user, useful for authenticated routes
public function setUp(): void
{
parent::setUp();
$user = User::first();
$this->actingAs($user);
}
public function testUploadFile()
{
$name = 'file.xlsx';
$path = 'absolute_directory_of_file/' . $name;
$file = new UploadedFile($path, $name, 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', null, true);
$route = 'route_for_upload';
// Params contains any post parameters
$params = [];
$response = $this->call('POST', $route, $params, [], ['upload' => $file]);
$response->assertStatus(200);
}
}
You can find this code at this link
Setup
/**
* #param $fileName
* #param $stubDirPath
* #param null $mimeType
* #param null $size
*
* #return \Illuminate\Http\UploadedFile
*/
public static function getTestingFile($fileName, $stubDirPath, $mimeType = null, $size = null)
{
$file = $stubDirPath . $fileName;
return new \Illuminate\Http\UploadedFile\UploadedFile($file, $fileName, $mimeType, $size, $error = null, $testMode = true);
}
Usage
$fileName = 'orders.csv';
$filePath = __DIR__ . '/Stubs/';
$file = $this->getTestingFile($fileName, $filePath, 'text/csv', 2100);
Folder Structure:
- MyTests
- TestA.php
- Stubs
- orders.csv
I want to click on an image opens the window for selecting a new image, and change the screen, now I want to change this picture in the bank, but this pending on an error:
Catchable Fatal Error: Argument 1 passed to
Delivve\WebBundle\Entity\User::setFile() must be an instance of
Symfony\Component\HttpFoundation\File\UploadedFile, string given,
called in
/home/delivve-webservice/src/Delivve/WebBundle/Controller/UserController.php
I do not know whether the error is in the path image that I'm taking?
$("#bundle_user_file").change(function () {
if (this.files && this.files[0]) {
var reader = new FileReader();
reader.onload = function (e) {
$('.active-img').attr('src', e.target.result);
ajax_formData(e.target.result);
};
reader.readAsDataURL(this.files[0]);
}
});
function ajax_formData(image) {
var path = "{{ path("submit_image_user", {"userId" : owner.id}) }}";
alert(image);
$.post(path, {image: image}, function (data) {
alert(data.message);
}, "json");
}
public function submitImageAction(Request $request, $userId){
$this->denyAccessUnlessGranted('ROLE_USER', null, 'Unable to access this page!');
$em = $this->getDoctrine()->getManager();
$entity = $this->getUser();
if ($entity->getId() != $userId) {
$response = new JsonResponse(
array(
'message' => "Não tem permissão de alterar esses dados"
), 400);
return $response;
}
if ($base64Content = $request->request->get('image')) {
$filePath = tempnam(sys_get_temp_dir(), 'UploadedFile');
$file = fopen($filePath, "w");
stream_filter_append($file, 'convert.base64-decode');
fwrite($file, $base64Content);
$meta_data = stream_get_meta_data($file);
$path = $meta_data['uri'];
fclose($file);
$entity->setFile($path);
$entity->upload();
$em->persist($entity);
$em->flush();
return new JsonResponse(array('message' => 'Success!'), 200);
}
$response = new JsonResponse(
array(
'message' => "imagem não encontrada"
), 400);
return $response;
}
these are the methods in my class
user.php
/**
* #return string
*/
public function getPictureUrl()
{
return $this->pictureUrl;
}
/**
* #param string $pictureUrl
*/
public function setPictureUrl($pictureUrl)
{
$this->pictureUrl = $pictureUrl;
}
/**
* Get file.
*
* #return UploadedFile
*/
public function getFile()
{
return $this->file;
}
/**
* Set file.
*
* #param UploadedFile $file
*/
public function setFile(UploadedFile $file = null)
{
$this->file = $file;
}
/**
* Relative path.
* Get web path to upload directory.
*
* #return string
*/
public function getUploadPath()
{
return 'uploads/pictures';
}
/**
* Absolute path.
* Get absolute path to upload directory.
*
* #return string
*/
protected function getUploadAbsolutePath()
{
return __DIR__ . '/../../../../web/' . $this->getUploadPath();
}
/**
* Relative path.
* Get web path to a cover.
*
* #return null|string
*/
public function getPictureWeb()
{
return null === $this->getPictureUrl()
? null
: $this->getUploadPath() . '/' . $this->getPictureUrl();
}
/**
* Get path on disk to a cover.
*
* #return null|string
* Absolute path.
*/
public function getPictureAbsolute()
{
return null === $this->getPictureUrl()
? null
: $this->getUploadAbsolutePath() . '/' . $this->getPictureUrl();
}
/**
* Upload a cover file.
*/
public function upload()
{
if (null === $this->getFile()) {
return;
}
$filename = $this->getFile()->getClientOriginalName();
$this->getFile()->move($this->getUploadAbsolutePath(), $filename);
$this->setPictureUrl($filename);
$this->setFile();
}
the file I get comes from the type base64, I have to turn it into a uploadfile
$file = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAA..."
I would greatly appreciate it if someone could help me to do this submition of asynchronous file.
public function submitImageAction(Request $request, $userId)
{
$this->denyAccessUnlessGranted('ROLE_USER', null, 'Unable to access this page!');
$em = $this->getDoctrine()->getManager();
$entity = $this->getUser();
if ($entity->getId() != $userId) {
$response = new JsonResponse(
array(
'message' => "Não tem permissão de alterar esses dados"
), 400);
return $response;
}
if ($base64Content = $request->request->get('image')) {
$dat = preg_split("/,/", $base64Content);
if (($fileData = base64_decode($dat[1])) === false) {
$response = new JsonResponse(
array(
'message' => "Base64 decoding error."
), 400);
return $response;
}
$fileName = $entity->getUploadPath() . "/" . uniqid() . ".jpeg";
if (file_put_contents($fileName, $fileData)) {
$entity->setPictureUrl($fileName);
$em->persist($entity);
$em->flush();
return new JsonResponse(array('message' => 'Success!'), 200);
}
}
$response = new JsonResponse(
array(
'message' => "imagem não encontrada"
), 400);
return $response;
}
the error was in the driver only need to hit like sending the path to the User