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

news2024/11/25 2:37:28

一、安装及引入

官网: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>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/783912.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【PWN · ret2libc】[BJDCTF 2020]babyrop

这题是经典的ret2libc&#xff0c;而且保护开的也不多&#xff0c;实际上&#xff0c;这篇博客的意义更大&#xff1a; 【PWN ret2libc】[2021 鹤城杯]babyof_Mr_Fmnwon的博客-CSDN博客 目录 前言 一、题目 二、思路 三、exp具体编写 总结 前言 简单而纯粹的ret2libc&am…

《DocRED: A Large-Scale Document-Level Relation Extraction Dataset》阅读笔记

引言 近期关注篇章级关系抽取&#xff0c;两个原因&#xff0c;一是之前做大规模知识抽取&#xff0c;遗留的一块内容就是篇章级关系抽取和事件抽取&#xff1b;另一个是大模型目前在抽取任务&#xff0c;特别是复杂抽取任务上表现不如其他NLP任务&#xff0c;这也引起我的研究…

5.2.6.字符设备驱动工作原理1

什么是模块&#xff1f;什么是驱动&#xff1f; 模块 驱动的雏形&#xff0c; 你要能操控硬件才叫驱动 在空的模块的基础上&#xff0c;安装驱动 5.2.6.1、系统整体工作原理 (1)应用层->API->设备驱动->硬件 (2)API&#xff1a;open、read、write、close等 (3)驱动…

20230720今天youtube上的中文字幕自动翻译成为英文的功能失效!

20230720今天youtube上的中文字幕自动翻译成为英文的功能失效&#xff01; 2023/7/20 12:42 &#xff1f;做YouTube挣钱吗&#xff1f; 115网盘 满了。最新 张家界 旅游的视频 放到 youtube就是 60岁/老了的时候的回忆&#xff01; 放到 大陆不保险&#xff01; 如需使用自动翻…

智能合约安全审计

智能合约安全审计的意义 智能合约审计用于整个 DeFi 生态系统&#xff0c;通过对协议代码的深入审查&#xff0c;可以帮助解决识别错误、低效代码以及这些问题。智能合约具有不可篡改的特点&#xff0c;这使得审计成为任何区块链项目安全流程的关键部分。 代码审计对任何应用…

Spring(二):更简单的存储与读取 Bean

通过上一章的Spring&#xff0c;我们基本实现了Spring 的读取与存储&#xff0c;但是在操作过程中&#xff0c;读取与存储并没有那么得“简单” 一套流程还是很复杂&#xff0c;所以&#xff0c;本章来介绍更加简单得读取与存储。 在 Spring 中想要更简单的存储和读取对象的核…

【C++】继承基础知识及简单应用,使用reportSingleClassLayout(在Visual Studio开发人员命令提示窗口)查看派生类详细信息

author&#xff1a;&Carlton tag&#xff1a;C topic&#xff1a;【C】继承基础知识及简单应用&#xff0c;使用reportSingleClassLayout&#xff08;在Visual Studio开发人员命令提示窗口&#xff09;查看派生类详细信息 website&#xff1a;黑马程序员C date&#xf…

MySQL 读写分离

目录 一、什么是读写分离&#xff1f; 二、为什么要读写分离呢&#xff1f; 三、什么时候要读写分离&#xff1f; 四、主从复制与读写分离 五、MySQL 读写分离原理 六、企业 使用MySQL 读写分离场景 1&#xff09;基于程序代码内部实现 2&#xff09;基于中间代理层实现…

比selenium体验更好的ui自动化测试工具: cypress介绍

话说 Cypress is a next generation front end testing tool built for the modern web. And Cypress can test anything that runs in a browser.Cypress consists of a free, open source, locally installed Test Runner and a Dashboard Service for recording your tests.…

【yolov7】训练自己的数据集-实践笔记

【yolov7】训练自己的数据集-实践笔记 使用yolov7训练自己的数据集&#xff0c;以RSOD数据集为例&#xff0c;图像数量976&#xff0c;一共四类。 yolov7源码&#xff1a;https://github.com/WongKinYiu/yolov7 同时在该网址下载好预训练文件&#xff0c;直接放到yolov7-main…

【每日随笔】马太效应 ② ( 马太效应因果分析 | 规模效应 | 齿轮效应 | 资源优势 | 抗风险能力 | 领先效应 )

文章目录 一、规模效应二、齿轮效应三、资源优势四、抗风险能力五、领先效应 在本文中 , 分析马太效应产生的原因 ; 一、规模效应 自然界中的规模效应 : 体型庞大的动物 , 如 大象 , 犀牛 , 雄狮 , 河马 , 很少被弱小的动物击败 , 都是自然死亡 , 老死 , 病死 , 同类厮杀 ; 经济…

多源BFS-- 矩阵距离

关于多源BFS&#xff0c;基本上就是单源BFS的简单升级了一下&#xff0c;比如在queue中队头开始时只有一个&#xff0c;我们通过这一个队头去推导其他的东西。而多源最短路就是队头一开始有1-n个可能的数&#xff0c;一个一个去BFS。 题目思路&#xff1a; 这个题就直接把所有的…

苹果开发“Apple GPT”AI科技迎来新格局

根据彭博社的马克・古尔曼&#xff08;Mark Gurman&#xff09;报道&#xff0c;苹果内部正在开发“Apple GPT”人工智能项目&#xff0c;足以媲美 OpenAI 的 ChatGPT &#xff0c;预计明年推出。就在彭博社消息发出之后&#xff0c;苹果股价上涨了2.3%&#xff0c;市值顶峰时增…

深入解析 Kubernetes 架构:掌握主节点、工作节点和容器运行时

&#x1f337;&#x1f341; 博主 libin9iOak带您 Go to New World.✨&#x1f341; &#x1f984; 个人主页——libin9iOak的博客&#x1f390; &#x1f433; 《面试题大全》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33…

【Linux后端服务器开发】协议定制(序列化与反序列化)

目录 一、应用层协议概述 二、序列化与反序列化 Protocal.h头文件 Server.h头文件 Client.h头文件 server.cpp源文件 client.cpp源文件 一、应用层协议概述 什么是应用层&#xff1f;我们通过编写程序解决一个个实际问题、满足我们日常需求的网络程序&#xff0c;都是应…

CMU15-445 2022 Fall 通关记录 —— Project 3:Query Execution(上篇)

前言 我在初次实现的时候并没有做 三个“选做”的排行榜任务&#xff0c;所以这只是上篇内容&#xff0c;等完成 Pro4 后再完成下篇。 Project 3: Query Execution Project #3 - Query Execution | CMU 15-445/645 :: Intro to Database Systems (Fall 2022) — 项目 #3 - 查…

R语言机器学习之影像组学分析的原理详解

概要 影像组学从常规医学图像中高通量提取大量的放射学定量数据&#xff0c;并以非侵入性方式探索它们与临床结果的相关性&#xff0c;在医学研究中得到广泛的应用。 01 影像组学&#xff08;Radiomics&#xff09;的概念&#xff1a; 影像组学&#xff08;Radiomics&#xff…

JVM堆内存介绍

一&#xff1a;JVM中内存 JVM中内存通常划分为两个部分&#xff0c;分别为堆内存与栈内存&#xff0c;栈内存主要用运行线程方法 存放本地暂时变量与线程中方法运行时候须要的引用对象地址。 JVM全部的对象信息都 存放在堆内存中。相比栈内存&#xff0c;堆内存能够所大的多&am…

图为科技应邀出席第38届中国计算机应用大会

第38届中国计算机应用大会&#xff08;CCF NCCA 2023&#xff09;暨2023年人工智能应用学术会议于7月16日-19日在苏州召开。 本次会议由中国计算机学会(CCF)主办&#xff0c;CCF计算机应用专业委员会承办&#xff0c;苏州大学、苏州科技大学、南京理工大学等单位协办&#xff0…

数字孪生搭高台,温控节能唱新戏

“孪生”的基本思想最早起源于1969年的阿波罗计划&#xff0c;通过留在地球上的航天器对发射到太空的航天器进行工作状态的仿真模拟&#xff0c;进而辅助航天员完成决策&#xff0c;减少各种操作结果的未知性。 从2002年开始&#xff0c;数字孪生的概念和定义在不同领域逐渐被提…