@itshixun/qckeditor-plugin-file
v1.0.1
Published
CKEditor 5 plugin for file upload
Downloads
279
Readme
@itshixun/qckeditor-plugin-file
CKEditor 5 插件:通用文件上传。在编辑器中插入可下载的文件链接,支持本地上传和直接插入已有文件,支持多文件上传和自动文件类型识别。
安装
npm install @itshixun/qckeditor-plugin-file
# 或
pnpm add @itshixun/qckeditor-plugin-file依赖 ckeditor5(peerDependencies,需自行安装)。
基本使用
1. 引入样式
import '@itshixun/qckeditor-plugin-file/dist/index.css';2. 注册插件
import { ClassicEditor } from 'ckeditor5';
import { QstFile } from '@itshixun/qckeditor-plugin-file';
const editor = await ClassicEditor.create(element, {
plugins: [
// ... 其他插件
QstFile,
],
toolbar: [
// ... 其他按钮
'uploadFile', // 工具栏按钮名称:上传文件
],
qstFile: {
upload: {
types: ['txt', 'rar', 'zip', 'doc', 'docx', 'pdf', 'xls', 'xlsx'],
maxSize: 100 * 1024 * 1024, // 100MB
},
progress: {
showProgressBar: true,
useReadOnlyLock: false,
},
},
});3. Vue 中使用
<script setup lang="ts">
import { ClassicEditor } from 'ckeditor5';
import { QstFile } from '@itshixun/qckeditor-plugin-file';
const config = {
editor: ClassicEditor,
plugins: [/* ... */, QstFile],
toolbar: ['bold', 'italic', 'uploadFile'],
qstFile: {
upload: {
types: ['pdf', 'docx', 'xlsx'],
maxSize: 50 * 1024 * 1024,
},
},
};
</script>
<template>
<ckeditor :editor="config.editor" :config="config" />
</template>配置项
| 属性 | 类型 | 必填 | 说明 |
|------|------|------|------|
| upload.types | string[] | 否 | 允许上传的文件扩展名列表,默认 ['txt', 'rar', 'zip', 'doc', 'docx', 'pdf', 'xls', 'xlsx'] |
| upload.maxSize | number | 否 | 最大文件大小(字节),超出会提示警告 |
| progress.showProgressBar | boolean | 否 | 是否显示上传进度条,默认 true |
| progress.useReadOnlyLock | boolean | 否 | 上传期间是否锁定编辑器为只读模式,默认 false |
上传配置
本插件依赖 CKEditor 5 的 FileRepository 插件处理文件上传。你需要配置一个自定义的 UploadAdapter,否则上传功能无法正常工作。
自定义 UploadAdapter 实现示例
import type { FileLoader, UploadAdapter } from 'ckeditor5';
class MyUploadAdapter implements UploadAdapter {
private loader: FileLoader;
private xhr?: XMLHttpRequest;
constructor(loader: FileLoader) {
this.loader = loader;
}
upload(): Promise<{ default: string }> {
return this.loader.file.then(
(file) =>
new Promise((resolve, reject) => {
this._initRequest();
this._initListeners(resolve, reject, file!);
this._sendRequest(file!);
})
);
}
abort(): void {
if (this.xhr) {
this.xhr.abort();
}
}
private _initRequest(): void {
const xhr = (this.xhr = new XMLHttpRequest());
xhr.open('POST', 'https://your-api.com/upload', true);
xhr.responseType = 'json';
}
private _initListeners(
resolve: (value: { default: string }) => void,
reject: (reason?: string) => void,
file: File
): void {
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;
if (!response || response.error) {
return reject(response?.error?.message || genericErrorText);
}
// 响应必须包含 default 字段(文件 URL)
resolve({ default: response.url });
});
if (xhr.upload) {
xhr.upload.addEventListener('progress', (evt) => {
if (evt.lengthComputable) {
loader.uploadTotal = evt.total;
loader.uploaded = evt.loaded;
}
});
}
}
private _sendRequest(file: File): void {
const data = new FormData();
data.append('file', file);
this.xhr!.send(data);
}
}在编辑器中注册 UploadAdapter
function MyUploadAdapterPlugin(editor: any) {
editor.plugins.get('FileRepository').createUploadAdapter = (loader: FileLoader) => {
return new MyUploadAdapter(loader);
};
}
const editor = await ClassicEditor.create(element, {
plugins: [
// ... 其他插件
MyUploadAdapterPlugin, // 必须先注册,再注册 QstFile
QstFile,
],
toolbar: ['uploadFile'],
});重要:
MyUploadAdapterPlugin必须在QstFile之前注册到plugins数组中- 上传响应必须返回
{ default: '文件URL' }格式,default字段会被提取为文件的url - 上传失败时会自动移除占位文件元素,并通过
Notification插件显示警告
命令说明
插件注册了两个命令,分别对应不同的使用场景:
fileInsert — 插入已有文件
通过文件 URL 直接插入已有文件(不上传):
editor.execute('fileInsert', {
name: '示例文档.pdf',
url: 'https://example.com/files/doc.pdf',
filetype: 'pdf', // 可选,默认 'document'
});uploadFile — 上传本地文件
通常由工具栏按钮自动触发,也可手动调用。支持多文件上传:
const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
const files = Array.from(fileInput.files!);
editor.execute('uploadFile', { file: files });输出 HTML
保存态(Data Downcast)
<a class="ck-file ck-qst-file ck-qst-file-pdf"
href="https://example.com/files/doc.pdf"
download="download"
data-filetype="pdf">
示例文档.pdf
</a>编辑态(Editing Downcast)
编辑态会渲染为 CKEditor 5 的 Widget,带有选中高亮和进度条:
<a class="ck-file ck-qst-file ck-qst-file-pdf ck-widget"
href="https://example.com/files/doc.pdf"
download="download"
data-filetype="pdf"
contenteditable="false">
示例文档.pdf
<!-- 上传时显示进度条 -->
<div class="ck-qst-file-progress-bar" style="width: 45%"></div>
</a>文件类型 class
插件根据 filetype 属性在 class 中添加类型标识,方便自定义不同文件类型的样式:
| 文件类型 | class 后缀 |
|----------|-----------|
| pdf | ck-qst-file-pdf |
| doc / docx | ck-qst-file-doc |
| xls / xlsx | ck-qst-file-xls |
| ppt | ck-qst-file-ppt |
| rar / zip | ck-qst-file-rar |
| img | ck-qst-file-img |
| video | ck-qst-file-video |
| 其他 | ck-qst-file-document |
数据兼容性
插件在 upcast(粘贴/加载 HTML)时兼容旧版格式:
| 新版本 | 旧版本 |
|--------|--------|
| <a class="ck-qst-file"> | <a class="ck-file"> |
旧版 HTML 会被自动转换为新版数据模型,保存时输出新版格式(同时保留 ck-file 类以兼容旧样式):
<!-- 旧版输入 -->
<a class="ck-file" href="..." download data-filetype="pdf">文档.pdf</a>
<!-- 新版输出 -->
<a class="ck-file ck-qst-file ck-qst-file-pdf" href="..." download data-filetype="pdf">文档.pdf</a>类型支持
导入插件后,EditorConfig 类型自动扩展,TypeScript 可直接识别 qstFile 配置:
const config: EditorConfig = {
// 无类型错误,qstFile 已声明
qstFile: {
upload: { types: ['pdf'], maxSize: 1024 * 1024 },
},
};导出 API
import {
QstFile, // 主插件(Editing + Upload)
QstFileEditing, // 编辑插件(Schema + Conversion + fileInsert 命令)
QstFileCommand, // 插入已有文件的命令
QstFileUpload, // 上传功能聚合插件
QstFileUploadEditing, // 上传编辑插件(自动上传处理 + 进度管理)
QstFileUploadUI, // 上传 UI 插件(工具栏按钮)
QstFileUploadProgress, // 上传进度条插件
QstFileUploadCommand, // 上传文件的命令
insertFile, // 工具函数:插入文件元素
isFileAllowed, // 工具函数:检查是否允许插入文件
getFileType, // 工具函数:根据 File 对象识别文件类型
type QstFileConfig,
type FileInsertCommandOptions,
type UploadFileCommandOptions,
} from '@itshixun/qckeditor-plugin-file';