我将在我的项目中使用ckeditor v5。我尝试使用图像插件,但未找到足够的信息。
如果您在这里查看演示,您可以轻松地通过拖放上传图像。但是当我尝试使用下载的zip文件时拖放图片时没有反应,也没有错误提示。
有没有办法在可下载版本中使用此图像支持?
我将在我的项目中使用ckeditor v5。我尝试使用图像插件,但未找到足够的信息。
如果您在这里查看演示,您可以轻松地通过拖放上传图像。但是当我尝试使用下载的zip文件时拖放图片时没有反应,也没有错误提示。
有没有办法在可下载版本中使用此图像支持?
内置了两个适配器:
For CKFinder which require you to install the CKFinder connectors on your server.
Once you have the connector installed on your server, you can configure CKEditor to upload files to that connector by setting the config.ckfinder.uploadUrl
option:
ClassicEditor
.create( editorElement, {
ckfinder: {
uploadUrl: '/ckfinder/core/connector/php/connector.php?command=QuickUpload&type=Files&responseType=json'
}
} )
.then( ... )
.catch( ... );
You can also enable full integration with CKFinder's client-side file manager. Check out the CKFinder integration demos and read more in the CKFinder integration guide.
For the Easy Image service which is a part of CKEditor Cloud Services.
You need to set up a Cloud Services account and once you created a token endpoint configure the editor to use it:
ClassicEditor
.create( editorElement, {
cloudServices: {
tokenUrl: 'https://example.com/cs-token-endpoint',
uploadUrl: 'https://your-organization-id.cke-cs.com/easyimage/upload/'
}
} )
.then( ... )
.catch( ... );
免责声明:这些是专有服务。
您还可以编写自己的上传适配器,以您想要的方式将文件发送到您的服务器(或任何您想要发送的地方)。
请参见自定义图像上传适配器指南,了解如何实现它。
一个示例(即没有内置安全性)的上传适配器可能如下所示:
class MyUploadAdapter {
constructor( loader ) {
// CKEditor 5's FileLoader instance.
this.loader = loader;
// URL where to send files.
this.url = 'https://example.com/image/upload/path';
}
// Starts the upload process.
upload() {
return new Promise( ( resolve, reject ) => {
this._initRequest();
this._initListeners( resolve, reject );
this._sendRequest();
} );
}
// Aborts the upload process.
abort() {
if ( this.xhr ) {
this.xhr.abort();
}
}
// Example implementation using XMLHttpRequest.
_initRequest() {
const xhr = this.xhr = new XMLHttpRequest();
xhr.open( 'POST', this.url, true );
xhr.responseType = 'json';
}
// Initializes XMLHttpRequest listeners.
_initListeners( resolve, reject ) {
const xhr = this.xhr;
const loader = this.loader;
const genericErrorText = 'Couldn\'t upload file:' + ` ${ loader.file.name }.`;
xhr.addEventListener( 'error', () => reject( genericErrorText ) );
xhr.addEventListener( 'abort', () => reject() );
xhr.addEventListener( 'load', () => {
const response = xhr.response;
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.
resolve( {
default: response.url
} );
} );
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() {
const data = new FormData();
data.append( 'upload', this.loader.file );
this.xhr.send( data );
}
}
然后可以像这样启用:
function MyCustomUploadAdapterPlugin( editor ) {
editor.plugins.get( 'FileRepository' ).createUploadAdapter = ( loader ) => {
return new MyUploadAdapter( loader );
};
}
ClassicEditor
.create( document.querySelector( '#editor' ), {
extraPlugins: [ MyCustomUploadAdapterPlugin ],
// ...
} )
.catch( error => {
console.log( error );
} );
注意:上述仅为上传适配器示例。因此,它没有内置的安全机制(如CSRF保护)。
我正在寻找如何使用这个控件的信息,但官方文档相当简略。经过多次尝试和错误,我终于让它正常工作了,因此我想分享一下。
最终,我在 Angular 8 中使用了 CKEditor 5 简单上传适配器,并且它可以很好地工作。但是,您需要创建一个自定义版本的 ckeditor,并安装上传适配器才行。这非常容易做到。 我假设您已经有了 ckeditor Angular 文件。
首先,创建一个新的Angular项目目录,将其命名为“cKEditor-Custom-Build”或其他名称。不要运行ng new(Angular CLI),而是使用npm获取您想要展示的编辑器的基本构建。 在此示例中,我正在使用经典编辑器。
https://github.com/ckeditor/ckeditor5-build-classic
前往 Github 并将项目克隆或下载到您的新构建目录中。
如果您使用 VS Code,请打开该目录并打开终端框,然后获取依赖项:
npm i
您现在已经拥有了基本构建,并且需要安装上传适配器。CKEditor有一个适配器。安装此软件包以获取简单的上传适配器:
npm install --save @ckeditor/ckeditor5-upload
完成后,打开项目中的ckeditor.js文件。它在“src”目录中。如果您已经在使用ckEditor,则其内容应该很熟悉。
将新的js文件导入到ckeditor.js文件中。此文件中会有很多导入项,请将其全部放置在底部。
import SimpleUploadAdapter from '@ckeditor/ckeditor5-upload/src/adapters/simpleuploadadapter';
然后将其导入到您的插件数组中。由于我正在使用经典编辑器,所以我的部分称为"ClassicEditor.builtinPlugins",请在TableToolbar旁边添加它。这样就完成了所有配置。无需在此结束处添加其他工具栏或配置。
构建您的ckeditor-custom-build。
npm run build
Angular的神奇之处会自行完成,并在您的项目中创建一个名为“build”的目录。这就是自定义构建的全部过程。
现在打开您的Angular项目,并创建一个新的构建目录。实际上,我将它放在“assets”子目录中,但您可以将其放在任何您可以引用的地方。
在“src/assets”中创建一个名为“ngClassicEditor”的目录,名称不重要,然后将构建文件复制到其中(刚刚创建的文件)。接下来,在要使用编辑器的组件中,添加一个导入语句,其中包含指向新构建的路径。
import * as Editor from '@app/../src/assets/ngClassicEditor/build/ckeditor.js';
即将完成...
最后一步是使用 API 端点配置上传适配器以上传图像。在组件类中创建一个配置。
public editorConfig = {
simpleUpload: {
// The URL that the images are uploaded to.
uploadUrl: environment.postSaveRteImage,
// Headers sent along with the XMLHttpRequest to the upload server.
headers: {
'X-CSRF-TOKEN': 'CSFR-Token',
Authorization: 'Bearer <JSON Web Token>'
}
}
};
我实际上在这里使用环境转换,因为从dev到production的URI会发生变化,但如果您愿意,您可以在其中硬编码一个直接的URL。
最后一步是在模板中配置编辑器以使用新的配置值。打开您的component.html文件并修改您的ckeditor编辑器标签。
<ckeditor [editor]="Editor" id="editor" [config]="editorConfig">
</ckeditor>
就是这样了,你完成了。测试、测试、测试。
我的 API 是 .Net API,如果您需要一些示例代码,我很乐意分享。我真的希望这能帮助到你。
对我来说它运行良好。感谢您的所有答案。这是我的实现。
myUploadAdapter.ts
import { environment } from "./../../../environments/environment";
export class MyUploadAdapter {
public loader: any;
public url: string;
public xhr: XMLHttpRequest;
public token: string;
constructor(loader) {
this.loader = loader;
// change "environment.BASE_URL" key and API path
this.url = `${environment.BASE_URL}/api/v1/upload/attachments`;
// change "token" value with your token
this.token = localStorage.getItem("token");
}
upload() {
return new Promise(async (resolve, reject) => {
this.loader.file.then((file) => {
this._initRequest();
this._initListeners(resolve, reject, file);
this._sendRequest(file);
});
});
}
abort() {
if (this.xhr) {
this.xhr.abort();
}
}
_initRequest() {
const xhr = (this.xhr = new XMLHttpRequest());
xhr.open("POST", this.url, true);
// change "Authorization" header with your header
xhr.setRequestHeader("Authorization", this.token);
xhr.responseType = "json";
}
_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;
if (!response || response.error) {
return reject(
response && response.error ? response.error.message : genericErrorText
);
}
// change "response.data.fullPaths[0]" with image URL
resolve({
default: response.data.fullPaths[0],
});
});
if (xhr.upload) {
xhr.upload.addEventListener("progress", (evt) => {
if (evt.lengthComputable) {
loader.uploadTotal = evt.total;
loader.uploaded = evt.loaded;
}
});
}
}
_sendRequest(file) {
const data = new FormData();
// change "attachments" key
data.append("attachments", file);
this.xhr.send(data);
}
}
component.html
<ckeditor
(ready)="onReady($event)"
[editor]="editor"
[(ngModel)]="html"
></ckeditor>
component.ts
import { MyUploadAdapter } from "./myUploadAdapter";
import { Component, OnInit } from "@angular/core";
import * as DecoupledEditor from "@ckeditor/ckeditor5-build-decoupled-document";
@Component({
selector: "xxx",
templateUrl: "xxx.html",
})
export class XXX implements OnInit {
public editor: DecoupledEditor;
public html: string;
constructor() {
this.editor = DecoupledEditor;
this.html = "";
}
public onReady(editor) {
editor.plugins.get("FileRepository").createUploadAdapter = (loader) => {
return new MyUploadAdapter(loader);
};
editor.ui
.getEditableElement()
.parentElement.insertBefore(
editor.ui.view.toolbar.element,
editor.ui.getEditableElement()
);
}
public ngOnInit() {}
}
import Fetch from './Fetch'; //my common fetch function
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 ) => {
const toBase64 = file => new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => resolve(reader.result);
reader.onerror = error => reject(error);
});
return toBase64(file).then(cFile=>{
return Fetch("admin/uploadimage", {
imageBinary: cFile
}).then((d) => {
if (d.status) {
this.loader.uploaded = true;
resolve( {
default: d.response.url
} );
} else {
reject(`Couldn't upload file: ${ file.name }.`)
}
});
})
} ) );
}
}
// ...
export default 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 );
};
}
并且在
import MyCustomUploadAdapterPlugin from '../common/ckImageUploader';
import CKEditor from '@ckeditor/ckeditor5-react';
import ClassicEditor from '@ckeditor/ckeditor5-build-classic';
<CKEditor
editor={ClassicEditor}
data={quesText}
placeholder="Question Text"
config={{extraPlugins:[MyCustomUploadAdapterPlugin]}} //use
/>
对于遇到XHR问题的人,你也可以使用fetch API,这似乎也能正常工作。
constructor(loader) {
// The file loader instance to use during the upload.
this.loader = loader;
this.url = '/upload';
}
request(file) {
return fetch(this.url, { // Your POST endpoint
method: 'POST',
headers: {
'x-csrf-token': _token
},
body: file // This is your file object
});
}
upload() {
const formData = new FormData();
this.loader.file.then((filenew) => {
console.log(filenew);
formData.append('file', filenew, filenew.name);
return new Promise((resolve, reject) => {
this.request(formData).then(
response => response.json() // if the response is a JSON object
).then(
success => console.log(success) // Handle the success response object
).catch(
error => console.log(error) // Handle the error response object
);
})
});
}
我使用了这个配置:
public editorConfig = {
simpleUpload: {
uploadUrl: environment.postSaveRteImage,
headers: {
'X-CSRF-TOKEN': 'CSFR-Token',
Authorization: 'Bearer <JSON Web Token>'
}
}