在正文之前要说一下,
其实我觉得在留言板用文字编辑器不是个好主意,
反而应该放在心情随笔的地方,
(但也不是不行, 譬如像Facebook这样, 有点像结合了留言板跟心情随笔功能)
不过因为之前有过失败的经验,
所以把最困难的放在最后面,
这两天研究很久终于研究出来了...
今天要解决的是图片上传,
用之前的程式虽然可以成功完成文字编辑器的编辑,
但是缺少了图片上传的部分,感觉好像少了什么?
所以今天要来做图片上传的部分,
收集了各种各样的资料,
剪剪贴贴修修补补之后,
终于完成了其中一种方式,
(有提供好几种, 但有的没想尝试, 有的尝试失败)
我把参考资料放在最后面,
有兴趣可以自己研究其他方式.
顺带一提,
我使用的是CKEditor 5,
跟以往的版本可能会有些许的不同.
首先js的部分加入一个自定义的物件
class MyUploadAdapter {constructor(loader) {// The file loader instance to use during the upload.this.loader = loader;}// 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", "/image", true); xhr.setRequestHeader('X-CSRF-TOKEN', '<?PHP echo csrf_token() ?>');xhr.responseType = "json";}// Initializes XMLHttpRequest listeners._initListeners(resolve, reject, file) {const xhr = this.xhr;const loader = this.loader;const genericErrorText = `无法上传档案: ${file.name}.`;xhr.addEventListener("error", () => reject(genericErrorText));xhr.addEventListener("abort", () => reject());xhr.addEventListener("load", () => { const response = xhr.response; console.log('response', 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); console.log('file:', 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 MyCustomUploadAdapterPlugin(editor) {editor.plugins.get("FileRepository").createUploadAdapter = loader => {// Configure the URL to the upload script in your back-end here!return new MyUploadAdapter(loader);};}
其中
xhr.open("POST", "/image", true);
里面的路径要写后端上传档案的路径
xhr.setRequestHeader('X-CSRF-TOKEN', '<?PHP echo csrf_token() ?>');
这是Laravel需要的SCRF的验证
另外也可以自己修改错误讯息(但是除非是500 Server Error, 如果传送成功, 错误讯息是从后端过来)
const genericErrorText = `无法上传档案: ${file.name}.`;
并且JavaScript要加入CKEditor的宣告
ClassicEditor .create(document.querySelector("#editor"), { extraPlugins: [MyCustomUploadAdapterPlugin], toolbar: ["heading", "|", "alignment:left", "alignment:center", "alignment:right", "alignment:adjust", "|", "bold", "italic", "blockQuote", "link", "|", "bulletedList", "numberedList", "imageUpload", "|", "undo", "redo"], }) .then(editor => { myEditor = editor; }) .catch(error => { console.error(error); });
然后要写后端接收的部分,
首先是web.php的部分
Route::group(['prefix' => '/'], function(){ //上传图片 Route::any('/image', 'HomeController@imageProcess');});
然后是图片接收的函式
app/Http/Controllers/HomeController.php
//接收档案上传public function imageProcess(){ header('Content-Type: application/pdf'); Log::notice('接收图片资料'); //接收输入资料 $input = request()->all(); $result = array(); Log::notice('接收图片'.print_r($input, true)); if(isset($input['upload'])) { $upload = $input['upload']; //档案副档名 $extension = $upload->getClientOriginalExtension(); //产生随机档案名称 $filename = uniqid().'.'.$extension; //相对路径 $relative_path = 'images/upload/'.$filename; //取得public目录下的完整位置 $fullpath = base_path('public_html/'.$relative_path); //允许的档案格式 switch($upload->getMimeType()) { case 'image/jpeg': case 'image/png': break; default: $result['error'] = array( 'message' => '很抱歉,只接受JPG和PNG档案', ); echo json_encode($result); exit; } //移动档案位置并改名称 move_uploaded_file($upload->getRealPath(),$relative_path); $result['url'] = '/'.$relative_path; echo json_encode($result); } else { $result['error'] = array( 'message' => '很抱歉,上传档案失败了', ); echo json_encode($result); }}
最后再附上成果图
到这里这个系列的文章差不多结束了,
虽然还有些东西想写,
不过就等年底再说了.
参考资料:
Simple upload adapter(官方文件)
[笔记]CKEditor加上CKFinder上传图档更方便
如何套用 CKEditor5 上传图片
CKEditor 5图片的上传方式
CKEditor 5 教学(三),上传图片至 Amazon S3