富文本CKEditor5简易入门,包括自定义上传图片(html版+vue.js)

一、安装及引入

官网:https://ckeditor.com/ckeditor-5/download/

我这边使用的是自定义构建,然后下载下来。
在这里插入图片描述

二、简单使用

引入js

<script src="../../../assets/plugins/ckeditor5/ckeditor.js"></script>

html:

<el-form-item label="服务内容">
     <textarea id="editor"></textarea>
</el-form-item>

js:

ClassicEditor
     .create( document.querySelector( '#editor' ))
     .then( editor => {
         this.editor = editor;
     } )
     .catch( error => {
         console.error( error );
     } );

获取富文本内容

var data = encodeURIComponent(this.editor.getData())

设置富文本内容

this.editor.setData(decodeURIComponent(content));

PS:这边进行编解码,便于存入数据库中

三、自动保存

ClassicEditor
    .create( document.querySelector( '#editor' ), {
        autosave: {
            save( editor ) {
                // 获取编辑器中的数据
                const data = editor.getData();
                // 将数据保存到本地
                localStorage.setItem( 'autosave', data );
            },
            waitingTime: 5000 // 设置保存间隔时间为 5}
    })
    .then( editor => {
        this.editor = editor;

        // 获取本地保存的数据
        const savedData = localStorage.getItem( 'autosave' );
        if ( savedData ) {
            // 提示用户是否恢复数据 (sweatAlert:https://sweetalert.js.org/)
            swal({
                title: "提示",
                text: "检测到上次编辑的内容,是否恢复?",
                icon: "warning",
                buttons: true,
                dangerMode: true,
            }).then((willDo) => {
                if (willDo) {
                    // 将数据恢复到编辑器中
                    editor.setData( savedData );
                } else {
                    // 清空本地保存的数据
                    localStorage.removeItem( 'autosave' );
                }
            });
        }
    } )
    .catch( error => {
        console.error( error );
    } );

四、图片上传

自定义UploadAdapter.js:

/**
 * 图片压缩处理,转换为等比的高800px的图像
 * @params file File类型的图片文件
 * @return Promise<file> 返回一个promise,值为一个压缩后的图片文件
 */
function imgCutdown(file) {
    return new Promise((resolve) => {
        const render = new FileReader();
        render.onload = function(progress) {
            const target = progress.target;
            if (!target) return;

            const reuslt = target.result;
            if (typeof reuslt === "string") {
                const image = new Image();
                image.src = reuslt;
                image.onload = function() {
                    const h = 800;
                    const rate = h / image.height;
                    const canvas = document.createElement("canvas");
                    const context = canvas.getContext("2d");
                    if (!context) return;
                    canvas.width = image.width * rate;
                    canvas.height = h;
                    context.drawImage(
                        image,
                        0,
                        0,
                        image.width,
                        image.height,
                        0,
                        0,
                        canvas.width,
                        canvas.height
                    );
                    canvas.toBlob(
                        function(b) {
                            const file = new File([b], "pic", {
                                type: "image/jpeg",
                            });
                            resolve(file);
                        },
                        "image/jpeg",
                        0.5
                    );
                };
            }
        };
        render.readAsDataURL(file);
    });
}

const uploadUrl = '/backend/upload/media';

// 自定义适配器类
class MyUploadAdapter {
    constructor(loader) {
        this.loader = loader;
    }

    upload() {
        return this.loader.file.then(
            (file) =>
                new Promise((resolve, reject) => {
                    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", uploadUrl, true);
        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
                );
            }

            resolve({
                default: response.data,
            });
        });

        if (xhr.upload) {
            xhr.upload.addEventListener("progress", (evt) => {
                if (evt.lengthComputable) {
                    loader.uploadTotal = evt.total;
                    loader.uploaded = evt.loaded;
                }
            });
        }
    }

    async _sendRequest(file) {
        const data = new FormData();

        // 判断如果上传图片大于1M,则进行压缩处理
        if (file.size > 1000 * 1024) {
            file = await imgCutdown(file);
        }

        // 上传参数就根据后端的处理而设置了
        data.append("file", file);
        data.append("name", file.name);
        data.append("group", "image");

        this.xhr.send(data);
    }
}

后端代码:

@RestController
@RequestMapping("/upload")
public class UploadController extends BaseController {

    @Autowired
    private QNYService qnyService;

    @Value("${QNY_DOMAIN}")
    private String QNY_DOMAIN;

    @RequestMapping("media")
    public ApiResponse uploadImage(HttpServletRequest request, String group) throws IOException {
        MultipartFile file = ((StandardMultipartHttpServletRequest) request).getFile("file");
        String key = "upload/" + group + "/" + this.get32UUID();
        qnyService.uploadFile(file, key);
        return ApiResponse.buildOk(QNY_DOMAIN+key);
    }
}

public class ApiResponse {
    int code;
    String msg;
    Object data;
}

QNYServiceImpl:

@Service
@Slf4j
public class QNYServiceImpl implements QNYService {
    @Value("${QNY_ACCESS_KEY}")
    String QNY_ACCESS_KEY;

    @Value("${QNY_SECRET_KEY}")
    String QNY_SECRET_KEY;

    private static final String BUCKET_NAME = "ehu";

    @Autowired
    private RedisService redisService;

    @Override
    public void deleteImg(String url) {
        Configuration cfg = new Configuration(Region.huadong());
        Auth auth = Auth.create(QNY_ACCESS_KEY, QNY_SECRET_KEY);
        BucketManager bucketManager = new BucketManager(auth, cfg);
        try {
            bucketManager.delete(BUCKET_NAME, url);
        } catch (QiniuException ex) {
            log.error("qiniuyun delete fail code {} msg {}", ex.code(), ex.response.toString(), ex);
        }
    }

    @Override
    public String getUploadToken() {
        String token = redisService.getQNYToken();
        if( token == null){
            Auth auth = Auth.create(QNY_ACCESS_KEY, QNY_SECRET_KEY);
            token = auth.uploadToken(BUCKET_NAME);
            redisService.setQNYToken(token);
        }
        return token;
    }

    @Override
    public String uploadUrlFile(String url, String prefix) {
        Configuration cfg = new Configuration(Region.huadong());
        Auth auth = Auth.create(QNY_ACCESS_KEY,QNY_SECRET_KEY);
        BucketManager bucketManager = new BucketManager(auth,cfg);
        try {
            FetchRet fetch = bucketManager.fetch(url, BUCKET_NAME, prefix + UuidUtil.get32UUID());
            return fetch.key;
        } catch (QiniuException e) {
            log.error("qiniuyun upload fail url{}", url, e);
            throw new RuntimeException(e);
        }
    }

    @Override
    public void uploadFile(MultipartFile file, String key) throws IOException {
        Configuration cfg = new Configuration(Region.huadong());
        Auth auth = Auth.create(QNY_ACCESS_KEY, QNY_SECRET_KEY);
        String upToken = auth.uploadToken(BUCKET_NAME);
        UploadManager uploadManager = new UploadManager(cfg);
        uploadManager.put(file.getBytes(), key, upToken);
    }
}

html:

<script src="../../../assets/plugins/ckeditor5/UploadAdapter.js"></script>

<script type="text/javascript">
...
	mounted() {
		ClassicEditor
		  .create( document.querySelector( '#editor' ))
		   .then( editor => {
		       editor.plugins.get('FileRepository').createUploadAdapter = (loader) => {
		           return new MyUploadAdapter(loader);
		       };
		       this.editor = editor;
		   } )
		   .catch( error => {
		       console.error( error );
		   } );
	}
</script>
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序猿S先森丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值