簡單使用CKEditor 5 及 使用PHP做後端接收上傳圖片

CKEditor 5最簡單的做法就是去Online Builder點一點,把你要的模組點完之後,按照sample裡面的範例放進你的網頁。但這次我有放圖片的需求,搞了半天才找到可行的做法。記得在Online Builder加進Image UploadSimple Upload Adapter

在加入CKEditor的語法前面加上下面程式碼

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', 'YOUR_UPLOAD_URL', true );
		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 MyCustomUploadAdapterPlugin( editor ) {
	editor.plugins.get( 'FileRepository' ).createUploadAdapter = ( loader ) => {
		// Configure the URL to the upload script in your back-end here!
		return new MyUploadAdapter( loader );
	};
}

把其中 YOUR_UPLOAD_URL 修改為後端接收圖片的網址。CKEditor原本只要用

ClassicEditor.create( document.querySelector( 'YOUR_QUERY' ),{});

把他修改成

ClassicEditor.create( document.querySelector( 'YOUR_QUERY'), {
	extraPlugins: [ MyCustomUploadAdapterPlugin ],
});

後端PHP程式碼

<?php
// Upload script for CKEditor.
// Use at your own risk, no warranty provided. Be careful about who is able to access this file
// The upload folder shouldn't be able to upload any kind of script, just in case.
// If you're not sure, hire a professional that takes care of adjusting the server configuration as well as this script for you.
// (I am not such professional)

// Configuration Options: Change these to alter the way files being written works
$overwriteFiles = false;

//THESE SETTINGS ONLY MATTER IF $overwriteFiles is FALSE

    //Seperator between the name of the file and the generated ending.
    $keepFilesSeperator = "-"; 

    //Use "number" or "random". "number" adds a number, "random" adds a randomly generated string.
    $keepFilesAddonType = "random"; 

    //Only usable when $keepFilesAddonType is "number", this specifies where the number starts iterating from.
    $keepFilesNumberStart = 1; 

    //Only usable when $keepFilesAddonType is "random", this specifies the length of the string.
    $keepFilesRandomLength = 4; 

//END FILE OVERWRITE FALSE SETTINGS

// Step 1: change the true for whatever condition you use in your environment to verify that the user
// is logged in and is allowed to use the script
if (true) {
    echo("You're not allowed to upload files");
    die(0);
}

// Step 2: Put here the full absolute path of the folder where you want to save the files:
// You must set the proper permissions on that folder (I think that it's 644, but don't trust me on this one)
// ALWAYS put the final slash (/)
$basePath = "/home/user/public_html/example/pages/projects/uploader/files/";

// Step 3: Put here the Url that should be used for the upload folder (it the URL to access the folder that you have set in $basePath
// you can use a relative url "/images/", or a path including the host "http://example.com/images/"
// ALWAYS put the final slash (/)
$baseUrl = "http://example.com/pages/projects/uploader/files/";

// Done. Now test it!



// No need to modify anything below this line
//----------------------------------------------------

// ------------------------
// Input parameters: optional means that you can ignore it, and required means that you
// must use it to provide the data back to CKEditor.
// ------------------------

// Optional: instance name (might be used to adjust the server folders for example)
$CKEditor = $_GET['CKEditor'] ;

// Required: Function number as indicated by CKEditor.
$funcNum = $_GET['CKEditorFuncNum'] ;

// Optional: To provide localized messages
$langCode = $_GET['langCode'] ;

// ------------------------
// Data processing
// ------------------------

// The returned url of the uploaded file
$url = '' ;

// Optional message to show to the user (file renamed, invalid file, not authenticated...)
$message = '';

// in CKEditor the file is sent as 'upload'
if (isset($_FILES['upload'])) {
    // Be careful about all the data that it's sent!!!
    // Check that the user is authenticated, that the file isn't too big,
    // that it matches the kind of allowed resources...
    $name = $_FILES['upload']['name'];

    //If overwriteFiles is true, files will be overwritten automatically.
    if(!$overwriteFiles) 
    {
        $ext = ".".pathinfo($name, PATHINFO_EXTENSION);
        // Check if file exists, if it does loop through numbers until it doesn't.
        // reassign name at the end, if it does exist.
        if(file_exists($basePath.$name)) 
        {
            if($keepFilesAddonType == "number") {
                $operator = $keepFilesNumberStart;
            } else if($keepFilesAddonType == "random") {
                $operator = bin2hex(openssl_random_pseudo_bytes($keepFilesRandomLength/2));
            }
            //loop until file does not exist, every loop changes the operator to a different value.
            while(file_exists($basePath.$name.$keepFilesSeperator.$operator)) 
            {
                if($keepFilesAddonType == "number") {
                    $operator++;
                } else if($keepFilesAddonType == "random") {
                    $operator = bin2hex(openssl_random_pseudo_bytes($keepFilesRandomLength/2));
                }
            }
            $name = rtrim($name, $ext).$keepFilesSeperator.$operator.$ext;
        }
    }
    move_uploaded_file($_FILES["upload"]["tmp_name"], $basePath . $name);

    // Build the url that should be used for this file   
    $url = $baseUrl . $name ;

    // Usually you don't need any message when everything is OK.
//    $message = 'new file uploaded';   
}
else
{
    $message = 'No file has been sent';
}
// ------------------------
// Write output
// ------------------------
if (!$message) {
    echo "{
         \"uploaded\": true,
         \"url\": \"$url\"
        }";
} else {
    echo "{
        \"uploaded\": false,
        \"error\": {
            \"message\": \"$message\"
        } 
    }";
}

第29行的判斷請依照需求處理。

$basePath$baseUrl 請依照實際伺服器狀況做調整。

參考資料:

Simple Upload Adapter
https://ckeditor.com/docs/ckeditor5/latest/features/images/image-upload/simple-upload-adapter.html

How to do CKEditor 5 Image Uploading?
https://stackoverflow.com/a/53060017/4865172

CKEditor, Image Upload (filebrowserUploadUrl)
https://stackoverflow.com/questions/2115302/ckeditor-image-upload-filebrowseruploadurl/44553006#44553006

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。