CKEditor4
当前版本为CKEditor4.13.0
下载CKEditor4
选择基础组件(Basic)
我们这里只用到了图片的上传,因此选择基础组件

2.选择图片上传插件

注意⚠️:选择File Browser
3.下载
将下载好的文件,解压后放入对应的目录中
引入并使用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>A Simple Page with CKEditor</title>
<!-- 确保引入的CKEditor文件路径正确 -->
<script src="../ckeditor.js"></script>
</head>
<body>
<form>
<textarea name="editor1" id="editor1" rows="10" cols="80">
This is my textarea to be replaced with CKEditor.
</textarea>
<script>
// 使用CKEditor替换 <textarea id="editor1">
// 实例化,使用默认配置
CKEDITOR.replace( 'editor1' );
</script>
</form>
</body>
</html>
相关配置
配置插件
1.展开ckeditor目录找到samples目录下的index.html文件,用浏览器打开

2.点击右上角的TOOLBAR CONFIGURATOR

3.勾选或者取消你想要的插件,然后点击Get toolbar config按钮,得到你想要的配置文件

4.复制该配置

5.找到ckeditor目录下的config.js文件

6.将刚复制的配置,粘贴到config.js文件中
添加上传tag
此时点击图片按钮我们发现,仍然不能上传图片

需要在刚刚的config.js中添加两个配置
config.extraPlugins = 'uploadimage' config.filebrowserImageUploadUrl = 'https://192.168.0.1/api/ECategoryDetail/UploadImg' //filebrowserImageUploadUrl 替换成你需要上传的服务器地址

我们再次点击图片按钮时候,便发现弹窗中多了上传的tag

隐藏浏览服务器按钮

我们可能无法浏览服务器上的图片,当我们点击浏览服务器按钮时,可能会出现空白页面

因此,为了实际体验,我们可能需要隐藏浏览服务器按钮

我们根据ckeditor/plugins/image2/dialogs路径找到image2.js文件
搜索关键字browseServer找到下行
label:f.lang.common.browseServer,hidden:!0
在其中添加style:"display:none",如下:
label:f.lang.common.browseServer,style:"display:none",hidden:!0
效果如下:

上传图片时,如何修改上传地址
在上面的配置里,我们添加了一条配置用来上传文件config.filebrowserImageUploadUrl如果不做任何修改,当我们点击上传到服务器时,他会默认使用该地址上传图片。
但这样很不灵活,通过查看文档,我们发现可以通过监听事件fileUploadRequest来做上传前的操作。
editor.on( 'fileUploadRequest', function( evt ) {
var fileLoader = evt.data.fileLoader,
formData = new FormData(),
xhr = fileLoader.xhr;
xhr.open( 'PUT', fileLoader.uploadUrl, true );
formData.append( 'upload', fileLoader.file, fileLoader.fileName );
fileLoader.xhr.send( formData );
// Prevented the default behavior.
evt.stop();
} );
修改fileLoader.uploadUrl即可,当然你也可以做其他操作,例如增加或修改消息头。
如何修改服务器的返回结果
服务器返回的结构有时候不是我们想要的,因此,可能需要对之进行修改。通过对事件fileUploadResponse进行监听即可。
editor.on( 'fileUploadResponse', function( evt ) {
// Prevent the default response handler.
evt.stop();
// Get XHR and response.
var data = evt.data,
xhr = data.fileLoader.xhr,
response = xhr.responseText.split( '|' );
if ( response[ 1 ] ) {
// An error occurred during upload.
data.message = response[ 1 ];
evt.cancel();
} else {
data.url = response[ 0 ];
}
} );
注意⚠️:
对其事件的监听一般放在CKeditor实例完成后instanceReady,因此如下使用:
CKEDITOR.on('instanceReady', function(e) {
console.log('CKEDITOR初始化', e.editor)
e.editor.widgets.on('instanceCreated', function(params) {
console.log('editor创建', params)
})
var upload = e.editor.uploadRepository
upload.on('instanceCreated', function(eve) {
alert('112233')
})
e.editor.on('change', function(change) {
console.log('change', change)
})
e.editor.on( 'fileUploadRequest', function( evt ) {
console.log(evt)
}
e.editor.on( 'fileUploadResponse', function( evt ) {
console.log(evt)
}
}
参考:
2022年最新完全版CKeditor v4.18.0,修改成可上传图片
1、修改配置文件config.js,增加如下选项:
config.extraPlugins = 'uploadimage'; config.filebrowserImageUploadUrl = '/widget/ckeditor/uploadImage';
2、Java服务端接收上传文件(图片)资源
(1) UploadConfig.java
package com.wanma.framework_web.config;
/**
* 上传文件配置
*/
public interface UploadConfig {
// 允许上传的文件类型
String[] imgTypes = new String[]{
"image/jpeg",
"image/gif",
"image/png"
};
}
(2) CkeditorForm.java
编辑器默认会传2个参数到后端:ckCsrfToken 和 upload
package com.wanma.framework_web.widget.ckeditor;
import lombok.Data;
import org.springframework.web.multipart.MultipartFile;
@Data
public class CkeditorForm {
private String ckCsrfToken;
private MultipartFile upload;
}
(3) CkeditorUploadController.java
package com.wanma.framework_web.widget.ckeditor;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.crypto.SecureUtil;
import com.wanma.framework_web.config.UploadConfig;
import com.wanma.framework_web.constant.UrlConst;
import com.wanma.framework_web.controller.BaseController;
import com.wanma.framework_web.entity.SysFile;
import com.wanma.framework_web.entity.SysUserFile;
import com.wanma.framework_web.service.ISysFileService;
import com.wanma.framework_web.service.ISysUserFileService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
@Slf4j
@Controller
public class CkeditorUploadController extends BaseController {
@Resource
private ISysFileService iBaseFileService;
@Resource
private ISysUserFileService iUserFileService;
/**
* 上传 图片
*/
@ResponseBody
@RequestMapping(UrlConst.widget_ckeditor_uploadImage)
public String uploadImage(
Model model,
@Value("${setting.uploadRootPath}") String uploadRootPath, // 上传文件保存路径
CkeditorForm ckeditorForm
) {
try {
// 创建根目录
if (!FileUtil.exist(uploadRootPath)) {
FileUtil.mkdir(uploadRootPath);
}
// 检测文件类型,只能上传常用图片类型
String contentType = ckeditorForm.getUpload().getContentType();
if (!ArrayUtil.contains(UploadConfig.imgTypes, contentType)) {
return """
{
"uploaded": 0,
"error": {
"message": "只能上传常用类型的图片"
}
}
""";
}
// 文件对象
MultipartFile file = ckeditorForm.getUpload();
// 扩展名
String fileName = file.getOriginalFilename();
String fileExt = FileUtil.extName(fileName);
// 是否已存在
String md5 = SecureUtil.md5(file.getInputStream());
SysFile oldFile = this.iBaseFileService.getByMd5(md5);
String fileUrl;
int fileId;
if (ObjectUtil.isEmpty(oldFile)) {
// 保存文件
String saveName = md5 + "." + fileExt;
String savePath = DateUtil.date().toString("yyyy/MM/dd");
String uploadDir = uploadRootPath + FileUtil.FILE_SEPARATOR + savePath;
FileUtil.mkdir(uploadDir);
String targeFilePath = uploadDir + FileUtil.FILE_SEPARATOR + saveName;
FileUtil.writeBytes(file.getBytes(), targeFilePath);
// 保存到数据库
SysFile baseFile = new SysFile();
baseFile.setName(file.getOriginalFilename());
baseFile.setType(file.getContentType());
baseFile.setExt(FileUtil.extName(file.getOriginalFilename()));
baseFile.setSize(file.getSize());
baseFile.setMd5(md5);
baseFile.setSavePath(savePath);
baseFile.setSaveName(saveName);
this.iBaseFileService.save(baseFile);
fileUrl = "/" + savePath + "/" + saveName;
fileId = baseFile.getId();
} else {
fileId = oldFile.getId();
fileUrl = "/" + oldFile.getSavePath() + "/" + oldFile.getSaveName();
}
// 插入 用户文件表
SysUserFile userFile = new SysUserFile();
userFile.setUserId(this.getLoginUserId(model));
userFile.setFileId(fileId);
userFile.setFileName(file.getOriginalFilename());
userFile.setFileUrl(fileUrl);
this.iUserFileService.save(userFile);
return """
{
"uploaded": 1,
"fileName": "#fileName#",
"url": "#fileUrl#"
}
""".replace("#fileName#", fileName)
.replace("#fileUrl#", fileUrl);
} catch (Exception e) {
log.error("上传图片发生错误:" + e.getMessage());
return """
{
"uploaded": 0,
"error": {
"message": "上传图片发生错误"
}
}
""";
}
}
}
【注意】
4.10版本之后,官方文档要求图片上传成功后,返回json格式,示例如下:
(1) 上传成功返回:
{
"uploaded": 1,
"fileName": "demo.jpg",
"url": "/files/demo.jpg"
}
{
"uploaded": 1,
"fileName": "test.jpg",
"url": "/files/test.jpg",
"error": {
"message": "A file with the same name already exists. The uploaded file was renamed to \"test.jpg\"."
}
}
(2) 上传失败返回:
{
"uploaded": 0,
"error": {
"message": "The file is too big."
}
}