如何在CKEditor 5中启用图像上传支持?

Luk*_*und 9 javascript ckeditor ckeditor5

我将使用ckeditor v5进入我的项目.我试图使用图片插件,但我找不到有关它的足够信息.

如果你在这里看到Demoe ,你可以使用Drag&Drop轻松上传图像.但是,当我尝试拖放图像时,我会尝试使用下载气囊拉链时没有任何反应.也没有错误.

有没有办法在downladable变体中使用此图像支持?

Rei*_*mar 20

是的,图像上传包含在所有可用的版本中.但是,为了使其工作,您需要配置一个现有的上载适配器或编写自己的上载适配器.简而言之,上传适配器是一个简单的类,其作用是将文件发送到服务器(以任何方式),并在完成后解析返回的promise.

您可以在官方图片上传指南中阅读更多内容,或查看下面可用选项的简短摘要.

官方上传适配器

有两个内置适配器:

免责声明:这些是专有服务.

定制上传适配器

您还可以编写自己的上传适配器,它将以您希望的方式(或您希望发送它们的任何位置)发送文件.

请参阅自定义图像上载适配器指南以了解如何实现它.

一个示例(即没有内置安全性)上传适配器可能如下所示:

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 );
    }
}
Run Code Online (Sandbox Code Playgroud)

然后可以像这样启用:

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 );
    } );
Run Code Online (Sandbox Code Playgroud)

注意:以上只是一个示例上传适配器.因此,它没有内置的安全机制(例如CSRF保护).

  • 似乎 loader.file.name 显示未定义。我无法获取文件名和扩展名。你能帮忙吗? (3认同)
  • 正如我的回答所说 - 您也可以编写自己的上传适配器.甚至还有一个第三方插件可以做到这一点(https://www.npmjs.com/package/ckeditor5-simple-upload). (2认同)

Dar*_*eet 10

我正在搜索有关如何使用此控件的信息,发现官方文档相当少。然而,经过多次反复试验,我确实让它工作了,所以我想我会分享。

最后,我在 Angular 8 中使用了 CKEditor 5 简单上传适配器,它工作得很好。但是,您确实需要创建安装了上传适配器的 ckeditor 的自定义构建。这很容易做到。我假设您已经拥有 ckeditor Angular 文件。

首先,创建一个新的 angular 项目目录并将其命名为“cKEditor-Custom-Build”或其他名称。不要运行 ng new (Angular CLI),而是使用 npm 来获取要显示的编辑器的基本构建。在这个例子中,我使用的是经典编辑器。

https://github.com/ckeditor/ckeditor5-build-classic
Run Code Online (Sandbox Code Playgroud)

转到 github 并将项目克隆或下载到新的闪亮构建目录中。

如果您使用 VS 代码,请打开目录并打开终端框并获取依赖项:

npm i
Run Code Online (Sandbox Code Playgroud)

现在您已经有了基础版本,您需要安装一个上传适配器。ckEditor 有一个。安装这个包以获得简单的上传适配器:

npm install --save @ckeditor/ckeditor5-upload
Run Code Online (Sandbox Code Playgroud)

..完成后,打开项目中的 ckeditor.js 文件。它在“src”目录中。如果您一直在玩 ckEditor,那么它的内容应该看起来很熟悉。

将新的 js 文件导入到 ckeditor.js 文件中。此文件中将有大量导入并将其全部删除。

import SimpleUploadAdapter from '@ckeditor/ckeditor5-upload/src/adapters/simpleuploadadapter';
Run Code Online (Sandbox Code Playgroud)

...接下来将导入添加到您的插件数组中。当我使用经典编辑器时,我的部分称为“ClassicEditor.builtinPlugins”,将其添加到 TableToolbar 旁边。就这样全部配置好了。此时不需要额外的工具栏或配置。

构建您的 ckeditor-custom-build。

npm run build
Run Code Online (Sandbox Code Playgroud)

Angular 的魔力将发挥作用,并在您的项目中创建一个“build”目录。它用于自定义构建。

现在打开你的 angular 项目并为你的新构建创建一个目录。我实际上把我的放在 assets 子目录中,但它可以放在任何你可以引用它的地方。

在“src/assets”中创建一个名为“ngClassicEditor”之类的目录,不管你怎么称呼它,然后将构建文件复制到其中(你刚刚创建的)。接下来,在要使用编辑器的组件中,添加带有新构建路径的 import 语句。

import * as Editor from '@app/../src/assets/ngClassicEditor/build/ckeditor.js';
Run Code Online (Sandbox Code Playgroud)

就快完成了...

最后一点是使用适配器应用于上传图像的 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>'
  }
}
Run Code Online (Sandbox Code Playgroud)

};

我实际上在此处使用环境转换,因为 URI 从开发更改为生产,但如果需要,您可以在其中硬编码直接 URL。

最后一部分是在模板中配置编辑器以使用新的配置值。打开你的 component.html 并修改你的 ckeditor 编辑器标签。

     <ckeditor [editor]="Editor" id="editor"  [config]="editorConfig">
      </ckeditor>
Run Code Online (Sandbox Code Playgroud)

就是这样。你完成了。测试,测试测试。

我的 API 是 .Net API,如果您需要一些示例代码,我很乐意与您分享。我真的希望这会有所帮助。


Ahm*_*lly 5

这对我来说效果很好。感谢您的所有回答。这是我的实现。


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);
  }
}

Run Code Online (Sandbox Code Playgroud)

组件.html

<ckeditor
  (ready)="onReady($event)"
  [editor]="editor"
  [(ngModel)]="html"
></ckeditor>
Run Code Online (Sandbox Code Playgroud)

组件.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() {}
}
Run Code Online (Sandbox Code Playgroud)


P L*_*MAN 5

在反应中

使用 MyCustomUploadAdapterPlugin 创建一个新文件

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 );
    };
}
Run Code Online (Sandbox Code Playgroud)

并在

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
  />
Run Code Online (Sandbox Code Playgroud)