I am developing a website with Laravel 8 and JavaScript. I use ckeditor 5 and it works fine. The problem occurs when I try to upload a photo. Photo upload is done but I get an error message and no photo is displayed. I get an error with the following title: "Cannot upload file: filename.jpg" I think I am not return the correct response to Frontend. I have these codes in the route/web.php file
Route::group(["middleware"=>"auth","prefix"=>"panel"],function (){
Route::post('/post/edit/{post_slug}', [PostController::class,"update"])->name("updatePostRoute");
Route::get('/post/edit/{post_slug}', [RenderPanelController::class,"renderPostEditPage"])->name("renderPostEditPageRoute");
Route::post('/post/ckeditor/upload', [PostController::class,"upload_image_cke"])->name('ckeditor.upload');
});
in the PostController
public function upload_image_cke(Request $request){
if ($request->hasFile('upload')) {
$originName = $request->file('upload')->getClientOriginalName();
$fileName = pathinfo($originName, PATHINFO_FILENAME);
$extension = $request->file('upload')->getClientOriginalExtension();
$fileName = $fileName . '_' . time() . '.' . $extension;
$request->file('upload')->move(public_path('media'), $fileName);
$CKEditorFuncNum = $request->input('CKEditorFuncNum');
$url = asset('media/' . $fileName);
$msg = 'upload successfully';
$response = "<script>window.parent.CKEDITOR.tools.callFunction($CKEditorFuncNum, '$url', '$msg')</script>";
@header('Content-type: text/html; charset=utf-8');
echo $response;
}
}
in the blade file:
<textarea type="text" class="form-control" name="content" id="content"></textarea>
<meta name="csrf-token" content="{{ csrf_token() }}">
<script src="https://cdn.ckeditor.com/ckeditor5/27.1.0/classic/ckeditor.js"></script>
<script>
ClassicEditor
.create( document.querySelector( '#content' ), {
ckfinder: {
uploadUrl: '{{route('ckeditor.upload')."?command=QuickUpload&type=Files&responseType=json"}}',
headers: {
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content'),
}
}
},{
alignment: {
options: [ 'right', 'right' ]
}} )
.then( editor => {
console.log( editor );
})
.catch( error => {
console.error( error );
})
</script>
What response do I need to return for the photo upload to work correctly?
I have another way to do it. Hope it works for you and many devs.
My way of doing it defines a storage path dynamically for each user ( $path_url
variable)
Note that you must run php artisan storage:link
on Terminal
web.php
Route::post('ckeditor/upload', 'App\Http\Controllers\CKEditorController@store')->name('ckeditor.upload');
CKEditorController - Method store
public function store(Request $request)
{
$path_url = 'storage/' . Auth::id();
if ($request->hasFile('upload')) {
$originName = $request->file('upload')->getClientOriginalName();
$fileName = pathinfo($originName, PATHINFO_FILENAME);
$extension = $request->file('upload')->getClientOriginalExtension();
$fileName = Str::slug($fileName) . '_' . time() . '.' . $extension;
$request->file('upload')->move(public_path($path_url), $fileName);
$url = asset($path_url . '/' . $fileName);
}
return response()->json(['url' => $url]);
}
Form
<textarea name="body" id="body" class="form-control"></textarea>
Now, you must define an "Adapter" to upload the files to Storage
<script src="https://cdn.ckeditor.com/ckeditor5/27.1.0/classic/ckeditor.js"></script>
<script>
//Define an adapter to upload the files
class MyUploadAdapter {
constructor(loader) {
// The file loader instance to use during the upload. It sounds scary but do not
// worry — the loader will be passed into the adapter later on in this guide.
this.loader = loader;
// URL where to send files.
this.url = '{{ route('ckeditor.upload') }}';
//
}
// Starts the upload process.
upload() {
return this.loader.file.then(
(file) =>
new Promise((resolve, reject) => {
this._initRequest();
this._initListeners(resolve, reject, file);
this._sendRequest(file);
})
);
}
// Aborts the upload process.
abort() {
if (this.xhr) {
this.xhr.abort();
}
}
// Initializes the XMLHttpRequest object using the URL passed to the constructor.
_initRequest() {
const xhr = (this.xhr = new XMLHttpRequest());
// Note that your request may look different. It is up to you and your editor
// integration to choose the right communication channel. This example uses
// a POST request with JSON as a data structure but your configuration
// could be different.
// xhr.open('POST', this.url, true);
xhr.open("POST", this.url, true);
xhr.setRequestHeader("x-csrf-token", "{{ csrf_token() }}");
xhr.responseType = "json";
}
// Initializes XMLHttpRequest listeners.
_initListeners(resolve, reject, file) {
const xhr = this.xhr;
const loader = this.loader;
const genericErrorText = `Couldn't upload file: ${file.name}.`;
xhr.addEventListener("error", () => reject(genericErrorText));
xhr.addEventListener("abort", () => reject());
xhr.addEventListener("load", () => {
const response = xhr.response;
// This example assumes the XHR server's "response" object will come with
// an "error" which has its own "message" that can be passed to reject()
// in the upload promise.
//
// Your integration may handle upload errors in a different way so make sure
// it is done properly. The reject() function must be called when the upload fails.
if (!response || response.error) {
return reject(response && response.error ? response.error.message : genericErrorText);
}
// If the upload is successful, resolve the upload promise with an object containing
// at least the "default" URL, pointing to the image on the server.
// This URL will be used to display the image in the content. Learn more in the
// UploadAdapter#upload documentation.
resolve({
default: response.url,
});
});
// Upload progress when it is supported. The file loader has the #uploadTotal and #uploaded
// properties which are used e.g. to display the upload progress bar in the editor
// user interface.
if (xhr.upload) {
xhr.upload.addEventListener("progress", (evt) => {
if (evt.lengthComputable) {
loader.uploadTotal = evt.total;
loader.uploaded = evt.loaded;
}
});
}
}
// Prepares the data and sends the request.
_sendRequest(file) {
// Prepare the form data.
const data = new FormData();
data.append("upload", file);
// Important note: This is the right place to implement security mechanisms
// like authentication and CSRF protection. For instance, you can use
// XMLHttpRequest.setRequestHeader() to set the request headers containing
// the CSRF token generated earlier by your application.
// Send the request.
this.xhr.send(data);
}
// ...
}
function SimpleUploadAdapterPlugin(editor) {
editor.plugins.get("FileRepository").createUploadAdapter = (loader) => {
// Configure the URL to the upload script in your back-end here!
return new MyUploadAdapter(loader);
};
}
//Initialize the ckeditor
ClassicEditor.create(document.querySelector("#body"), {
extraPlugins: [SimpleUploadAdapterPlugin],
}).catch((error) => {
console.error(error);
});
</script>
I hope I've helped. A hug!
in the PostController
public function upload_image_cke(Request $request){
if ($request->hasFile('upload')) {
$originName = $request->file('upload')->getClientOriginalName();
$fileName = pathinfo($originName, PATHINFO_FILENAME);
$extension = $request->file('upload')->getClientOriginalExtension();
$fileName = $fileName . '_' . time() . '.' . $extension;
$request->file('upload')->move(public_path('media'), $fileName);
$url = asset('media/' . $fileName);
return response()->json(['fileName' => $fileName, 'uploaded'=> 1, 'url' => $url]);
}
}
in the blade file:
<script>
ClassicEditor
.create( document.querySelector( '#content' ), {
ckfinder: {
uploadUrl: '{{route('ckeditor.upload').'?_token='.csrf_token()}}'
}
},{
alignment: {
options: [ 'right', 'right' ]
}} )
.then( editor => {
console.log( editor );
})
.catch( error => {
console.error( error );
})
</script>
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.