Laravel / vue-froala-wysiwyg integration - laravel

I'll like to implemente the image upload system within my Laravel/VueJS project but I can't find a right way to do so. How can I set up my Controller function in order to handle this upload?
Edit:
This is my Editor configuration:
config: {
imageUploadParam: 'imageFile',
imageUploadURL: '/froala/upload/image',
imageUploadMethod: 'POST',
imageMaxSize: 5 * 1024 * 1024,
imageAllowedTypes: ['jpeg', 'jpg', 'png'],
}
And this is the function that handles the request:
public function uploadImage(Request $request)
{
$file = $request['imageFile'];
$name = $file->getClientOriginalName();
$name = strtolower(str_replace(' ', '', $name));
$path = $file->hashName();
$image = Image::make($file);
Storage::put("/threads/{$path}", (string) $image->encode());
$multimedia = Multimedia::create([
'name' => $name,
'path' => $path
]);
return ['link' => $multimedia->path];
}
I am using the Intervention Image library to handle the image upload.
Edit 2:
I'm getting an 419 error related with the csrf token. So, how can i pass it to the function? I know how to get it but using the imageUploadParams configuration of the editor is not working:
imageUploadParams: {
csrf: this.csrf
}
csrf: document.querySelector('meta[name="csrf-token"]').getAttribute('content'),

You need to pass the correct X-CSRF-TOKEN value to avoid the 419 error.
First check that you have the token defined the in the meta header with something like:
<meta name="csrf-token" content="{{ csrf_token() }}">
Early in your VueJS add:
var csrf_token = $('meta[name="csrf-token"]').attr('content');
Then add the following to your Froala config section:
config: {
requestHeaders: {
'X-CSRF-TOKEN': csrf_token
},
Media files should now pass through to your media upload function in Laravel.

From the documentation :
When an image is inserted into the WYSIWYG HTML editor, a HTTP request is automatically done to the server.
The specific URL that the request will be made to can be specified using the imageUploadURL config option.
Setup your routes.php file to properly direct the request to your controller of choice :
Route::post('/upload', 'FilesController#store');
Then, in your controller you can handle the image upload like you would normally. The important part is that you return the path to the file after you've saved it.
The returned JSON needs to look like: { "link": "path/to/image.jpg" }
Here is an example of what that could look like :
public function store(){
$filepath = request()->file('file')->store('images', 'public');
return ['link' => $filepath];
}
Of course, feel free to do any kind of validation or processing that you need.

instand of
imageUploadParams: {
csrf: this.csrf
}
use this
imageUploadParams: {
_token: this.csrf
}
Check this out From Documentation

Related

Front End File Upload using Vue and Winter Cms

I'm trying to upload images from a Vue frontend via Illuminate/Http/Request to WinterCMS.
Vue finds the file and i can console.log the File object, but I'm unsure how to get this over the api. for example I've tried
public function saveImage(Request $req){
$images = $req->files('images');
}
which doesn't work, nor does
public function saveImage(Request $req){
$images = $req['images'];
}
I'm using a controller to handle my routes eg:
Route::post('/saveImage', 'Author\Project\Controllers\ProductControl#saveImage');
I've added an attachOne relation to the plugin as usual and my form has enctype="multipart/form-data"
I've had this problem before and got around it by converting images to base64 but this project will have quite a few images and I don't want to go down that route again.
Any suggestions greatly appreciated
You can send images as regular post and use regular $request->file('images') method in your Laravel controller.
You can use Javascript FormData object. For example;
<div>
<input type="file" #change="handleImages" multiple>
<button #click="uploadImages">Upload!</button>
</div>
data: () => ({
images: []
}),
methods: {
handleImages (event) {
this.images = event.target.files
},
uploadImages () {
const formData = new FormData();
for (const i of Object.keys(this.images)) {
formData.append('images', this.images[i])
}
axios.post('/saveImage', formData, {
}).then((res) => {
console.log(res)
})
}
}

Get image from blob Laravel vue

First of all, this is the start of where I am at as a similar post
Store blob as a file in S3 with Laravel
I am sending a photo from VueJS to Laravel. It is coming as multipart/form-data.
Vue Code:
export default {
emits: ['onClose'],
props: ['isOpen'],
data: function() {
return {
serverOptions: {
process: (fieldName, file, metadata, load, error) => {
const formData = new FormData();
formData.append(fieldName, file, file.name);
axios({
method: "POST",
url: '/chat/room/upload',
data: formData,
headers: {
"Content-Type": "multipart/form-data"
}
})
.then(() => {
load();
})
.catch(() => {
error();
});
}
},
files: [],
};
},
methods: {
handleFilePondInit: function () {
console.log('FilePond has initialized');
// example of instance method call on pond reference
this.$refs.pond.getFiles();
console.log(this.$refs.pond.getFiles());
},
},
Laravel Controller:
public function uploadImage(Request $request)
{
// This is what this is SUPPOSED to do. Grab the file from the frontend
// Bring it here. Store it in S3, return the path with the CDN URL
// Then store that URL into the DB as a message. Once that is done, then
// Broadcast the message to said room.
if ($request->has('upload')) {
$files = $request->get('photo');
$urls = [];
foreach ($files as $file) {
$filename = 'files/' . $file['name'];
// Upload File to s3
Storage::disk('digitalocean')->put($filename, $file['blob']);
Storage::disk('digitalocean')->setVisibility($filename, 'public');
$url = Storage::disk('digitalocean')->url($filename);
$urls[] = $url;
}
return response()->json(['urls' => $urls]);
}
// broadcast(new NewChatMessage($newMessage))->toOthers();
// return $newMessage;
}
First: I want to state that if there is something wrong with the current code, just know its because ive been playing around with this for 3 hours now and been trying anything. I am sure at one point I had it close but somehow screwed it up along the way so I am more looking for fresh eyes to show me my error.
That being said, the other part to take into account is in DevTools under Network I can clearly see the blob and can load it up, I can also see the "upload" item and under there the form data which shows the following
------WebKitFormBoundary7qD7xdmiQO9U1Ko0
Content-Disposition: form-data; name="photo"; filename="6A8B48B4-F546-438E-852E-C24340525C20_1_201_a.jpeg"
Content-Type: image/jpeg
------WebKitFormBoundary7qD7xdmiQO9U1Ko0--
it clearly also shows photo: (binary) so I am completely confused as to what I am doing wrong. The ULTIMATE goal here is to get the image, store it as public in S3/DigitalOcean then grab the public URL to the file and store in the DB.
Any help would be GREATLY appreciated!

How to convert base64 image to UploadedFile Laravel

Vue Version : 2.6.10
Laravel Version : 6.0
I am using this vue upload package and everything is ok on client side (at least I think so). But on the server side, where I am using the laravel, have some problem.
Here is my vue send method:
setImage: function (file) {
let formData = new FormData();
formData.append('file', file);
axios.post(upload_route, formData , {
headers: { 'Content-Type': 'multipart/form-data' }
})
.then(response => {
// upload successful
})
.catch(error => console.log(error));
},
And this is my server side method:
public function upload(Request $request){
$path = $request->file('file')->store('avatars');
return response('upload success' , 200);
}
When I upload the file to the server, it gives me this error:
"message": "Call to a member function store() on null",
The file object I am sending in the setImage function is something like this (if I log it with console.log):
data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/2wBDAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE...
I believe file parameter on setImage is not a File object. So the $request->file('file') is null, because you attach a string (base64), not a file.
You told us that output from console.log is base64 path, then you need to convert that (base64) to file.
Since you're using Laravel, here is the technique:
use Illuminate\Support\Str;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;
use Symfony\Component\HttpFoundation\File\File;
.....
$base64File = $request->input('file');
// decode the base64 file
$fileData = base64_decode(preg_replace('#^data:image/\w+;base64,#i', '', $base64File));
// save it to temporary dir first.
$tmpFilePath = sys_get_temp_dir() . '/' . Str::uuid()->toString();
file_put_contents($tmpFilePath, $fileData);
// this just to help us get file info.
$tmpFile = new File($tmpFilePath);
$file = new UploadedFile(
$tmpFile->getPathname(),
$tmpFile->getFilename(),
$tmpFile->getMimeType(),
0,
true // Mark it as test, since the file isn't from real HTTP POST.
);
$file->store('avatars');
Update
Since you're using vue-image-upload-resize, I check the documentation that it has built in function to change the output from base64 to blob, so you can just:
<image-uploader
...
output-format="blob"
... />
<?php
if (preg_match('/^data:image\/(\w+);base64,/', $data, $type)) {
$data = substr($data, strpos($data, ',') + 1);
$type = strtolower($type[1]); // jpg, png, gif
​
if (!in_array($type, [ 'jpg', 'jpeg', 'gif', 'png' ])) {
throw new \Exception('Image Type is Not valid');
}
$data = base64_decode($data);
if ($data === false) {
throw new \Exception('Failed to Decode BASE64');
}
} else {
throw new \Exception('Data Not Matched With Image Data');
}
file_put_contents("image_name.{$type}", $data);//save decoded data as image
?>
This decode and preg_match always worked for me whenever i have image like
data:image/jpeg;base64
Pass this data as $data and your extension type as $type
Found better solution, when temp files deletes after script terminate
class FileHelper
{
public static function fromBase64(string $base64File): UploadedFile
{
// Get file data base64 string
$fileData = base64_decode(Arr::last(explode(',', $base64File)));
// Create temp file and get its absolute path
$tempFile = tmpfile();
$tempFilePath = stream_get_meta_data($tempFile)['uri'];
// Save file data in file
file_put_contents($tempFilePath, $fileData);
$tempFileObject = new File($tempFilePath);
$file = new UploadedFile(
$tempFileObject->getPathname(),
$tempFileObject->getFilename(),
$tempFileObject->getMimeType(),
0,
true // Mark it as test, since the file isn't from real HTTP POST.
);
// Close this file after response is sent.
// Closing the file will cause to remove it from temp director!
app()->terminating(function () use ($tempFile) {
fclose($tempFile);
});
// return UploadedFile object
return $file;
}
}
From https://gist.github.com/waska14/8b3bcebfad1f86f7fcd3b82927576e38

How to access image from storage

I have a file saved in storage/app/uploads and I would like get this this image saved and show with img src tag.
But I can't
I am trying:
public function getSlider(Request $request)
{
$slider = Slider::find( $request->input('id') );
$slider->imagem = storage_path('storage/app/uploads/'.$slider->imagem);
return response()->json( $slider );
}
And I am trying show with jquery and javascript
function getSliders() {
$.ajaxSetup({
headers: {'X-CSRF-TOKEN' : $('#token').val() }
})
$.ajax({
url: "{{ route('site.getSlider') }}",
type: "post",
dataType: "json",
data: {
id: $('#id').val()
}
}).done( r => {
console.log('r',r)
$('#description').val( r.description )
setImage( r.image )
})
}
And setting Image
function setImage( image ){
var newImage = document.createElement('img');
newImage.setAttribute('src', image);
newImage.width = 500;
newImage.heigth = 200;
document.getElementById("photo").innerHTML = newImage.outerHTML;
}
When I want access image from public/img I use
<img src="{{ URL('img/myImage.png') }}">
But I want to access from storage/app/uploads
This return the file route (no url) (if you see the log it says /var/www.... etc)
storage_path('storage/app/uploads/'.$slider->imagem);
You should use asset helper to create URL of image
asset( 'storage/app/uploads/'.$slider->imagem);
Before this you need to run this command to create symbolic link to storage folder from public folder.
php artisan storage:link
Please try this and let me know how it works :)
try save your images in public folder instead of storage
its simple you can access it by using public_path() function.
Firstly, run this command:
php artisan storage:link
Then in any view you can access your image with the help of url helper.
url('storage/app/uploads');

broadcast to others and dont broadcast to current user are not working

In my TaskList.vue I have the code below:
<template>
<div>
<ul>
<li v-for="task in tasks" v-text="task"></li>
</ul>
<input type="text" v-model="newTask" #blur="addTask">
</div>
</template>
<script>
export default {
data(){
return{
tasks: [],
newTask: ''
}
},
created(){
axios.get('tasks')
.then(
response => (this.tasks = response.data)
);
Echo.channel('task').listen('TaskCreated', (data) => {
this.tasks.push(data.task.body);
});
},
methods:{
addTask(){
axios.post('tasks', { body: this.newTask })
this.tasks.push(this.newTask);
this.newTask = '';
}
}
}
</script>
When I hit the axios.post('tasks') end point, I got duplicate result in my current tab that i input the value and the another tab got only 1 value which is correct.
To avoid this, I tried to use
broadcast(new TaskCreated($task))->toOthers();
OR
I put $this->dontBroadcastToCurrentUser() in the construct of my TaskCreated.php
However, both methods are not working. Any idea why?
The image below is from network tab of mine. One of it is pending, is that what caused the problem?
https://ibb.co/jpnsnx (sorry I couldn't post image as it needs more reputation)
I solved this issue on my Laravel Spark project by manually sending the X-Socket-Id with the axios post.
The documentation says the header is added automatically if you're using vue and axios, but for me (because of spark?) it was not.
Below is an example to show how I manually added the X-Socket-Id header to an axios post using the active socketId from Laravel Echo:
axios({
method: 'post',
url: '/api/post/' + this.post.id + '/comment',
data: {
body: this.body
},
headers: {
"X-Socket-Id": Echo.socketId(),
}
})
Laravel looks for the header X-Socket-ID when using $this->dontBroadcastToCurrentUser(); Through this ID, Laravel identifies which user (current user) to exclude from the event.
You can add an interceptor for requests in which you can add the id of your socket instance to the headers of each of your requests:
/**
* Register an Axios HTTP interceptor to add the X-Socket-ID header.
*/
Axios.interceptors.request.use((config) => {
config.headers['X-Socket-ID'] = window.Echo.socketId() // Echo instance
// the function socketId () returns the id of the socket connection
return config
})
window.axios.defaults.headers.common['X-Socket-Id'] = window.Echo.socketId();
this one work for me..!!

Resources