- 前端: CKEditor 5 (推荐,因为它更现代化、功能更强大)
- 后端: Spring Boot (Java)
- 图片存储: 服务器本地文件系统 (最简单的方式)
整体思路
- 前端配置 CKEditor: 初始化 CKEditor,并配置一个自定义的上传按钮。
- 前端处理上传: 当用户点击上传按钮时,通过
fetchAPI 将图片文件发送到我们后端创建的接口。 - 后端接收文件: Spring Boot 创建一个
@PostMapping接口,接收multipart/form-data格式的文件。 - 后端处理并存储: 后端接收文件,进行校验(如大小、类型),然后将其保存到服务器的指定目录。
- 后端返回响应: 上传成功后,后端返回一个 JSON 响应,包含图片的访问 URL,CKEditor 会根据这个 URL 将图片插入到编辑器中。
第一步:后端实现 (Spring Boot)
我们将创建一个 Spring Boot 应用来处理文件上传。

项目依赖 (pom.xml)
确保你的 pom.xml 包含了 Spring Web 和 Lombok(可选,用于简化代码)依赖。
<dependencies>
<!-- Spring Boot Starter Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Lombok (可选,用于简化 getter/setter) -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
创建文件上传目录
在 src/main/resources 目录下创建一个 static/uploads 目录,上传的图片将保存在这里,Spring Boot 默认会从 static 目录下提供静态文件访问。
src/
└── main/
└── resources/
└── static/
└── uploads/ <-- 图片存放目录
创建文件上传配置类 (FileUploadConfig.java)
这个类用于配置文件上传的大小限制等。
package com.example.ckeditordemo.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties(prefix = "file.upload") // 从 application.yml 中读取配置
public class FileUploadConfig {
private String uploadDir;
private long maxFileSize; // 单位: bytes
// Getters and Setters
public String getUploadDir() {
return uploadDir;
}
public void setUploadDir(String uploadDir) {
this.uploadDir = uploadDir;
}
public long getMaxFileSize() {
return maxFileSize;
}
public void setMaxFileSize(long maxFileSize) {
this.maxFileSize = maxFileSize;
}
}
在 application.yml 中添加配置
在 src/main/resources/application.yml 文件中添加以下配置:

server:
port: 8080
file:
upload:
# 上传目录,注意要和 FileUploadConfig 中的 prefix 对应
# 这里我们指向 static/uploads 目录
upload-dir: uploads
# 最大文件大小,这里是 2MB
max-file-size: 2097152
创建文件上传控制器 (FileUploadController.java)
这是核心部分,负责处理文件上传请求。
package com.example.ckeditordemo.controller;
import com.example.ckeditordemo.config.FileUploadConfig;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.UUID;
@Slf4j
@RestController
@RequestMapping("/api/upload")
public class FileUploadController {
@Autowired
private FileUploadConfig fileUploadConfig;
@PostMapping(value = "/image", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<?> uploadImage(@RequestParam("upload") MultipartFile file) {
try {
// 1. 检查文件是否为空
if (file.isEmpty()) {
return ResponseEntity.badRequest().body("请选择一个文件。");
}
// 2. 检查文件大小
if (file.getSize() > fileUploadConfig.getMaxFileSize()) {
return ResponseEntity.badRequest().body("文件大小超过限制。");
}
// 3. 检查文件类型 (简单示例,只允许图片)
String contentType = file.getContentType();
if (contentType == null || !contentType.startsWith("image/")) {
return ResponseEntity.badRequest().body("只允许上传图片文件。");
}
// 4. 获取原始文件名
String originalFilename = file.getOriginalFilename();
// 5. 生成新的唯一文件名,防止覆盖
String fileExtension = originalFilename.substring(originalFilename.lastIndexOf("."));
String newFilename = UUID.randomUUID().toString() + fileExtension;
// 6. 构建文件存储路径
// 注意:fileUploadConfig.getUploadDir() 返回的是 "uploads"
// 我们需要将其拼接成完整的服务器路径
Path uploadPath = Paths.get("src/main/resources/static", fileUploadConfig.getUploadDir());
// 如果目录不存在,则创建
if (!Files.exists(uploadPath)) {
Files.createDirectories(uploadPath);
}
Path destination = uploadPath.resolve(newFilename);
// 7. 保存文件
Files.copy(file.getInputStream(), destination);
// 8. 返回 CKEditor 期望的响应格式
String fileUrl = "/uploads/" + newFilename; // 静态资源访问路径
String responseJson = String.format(
"{\"url\":\"%s\",\"uploaded\":\"%s\"}",
fileUrl,
"true"
);
return ResponseEntity.ok(responseJson);
} catch (IOException e) {
log.error("文件上传失败", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("文件上传失败。");
}
}
}
重要说明:
- CKEditor 的响应格式: CKEditor 5 对图片上传的响应有特定的 JSON 格式要求,成功时,它期望收到
{"url": "图片URL", "uploaded": "true"}。 - 静态资源路径: 我们将文件保存在
static/uploads下,Spring Boot 会自动将/uploads/...的请求映射到这个目录,所以返回的url应该是/uploads/文件名。
第二步:前端实现 (CKEditor 5)
引入 CKEditor 5
你可以使用 CDN 或 npm/yarn,这里我们使用 CDN,因为它最简单。
在 HTML 页面的 <head> 中引入 CKEditor 5 的样式和脚本:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">CKEditor 5 图片上传示例</title>
<!-- CKEditor 5 样式 -->
<link rel="stylesheet" href="https://cdn.ckeditor.com/ckeditor5/39.0.1/ckeditor5.css">
<!-- 自定义样式,让编辑器占满屏幕 -->
<style>
body {
font-family: sans-serif;
}
.editor-container {
width: 100%;
max-width: 1000px;
margin: 20px auto;
border: 1px solid #ccc;
}
</style>
</head>
<body>
<h1>CKEditor 5 图片上传示例</h1>
<div id="editor" class="editor-container"></div>
<!-- CKEditor 5 脚本 -->
<script src="https://cdn.ckeditor.com/ckeditor5/39.0.1/ckeditor5.js"></script>
<!-- 初始化编辑器的脚本 -->
<script>
ClassicEditor
.create(document.querySelector('#editor'), {
// 配置项
toolbar: {
items: [
'heading',
'|',
'bold',
'italic',
'link',
'bulletedList',
'numberedList',
'|',
'outdent',
'indent',
'|',
'imageUpload', // 添加图片上传按钮
'blockQuote',
'insertTable',
'undo',
'redo'
]
},
// 图片上传配置
image: {
toolbar: [
'imageTextAlternative',
'imageStyle:alignLeft',
'imageStyle:alignCenter',
'imageStyle:alignRight',
'imageStyle:side'
],
// 自定义上传适配器
upload: {
// 你的后端上传接口地址
// 确保这个地址能被你的前端页面访问到(CORS问题)
// 如果前端和后端同源,一般没问题。
// 如果是前后端分离,后端需要配置CORS。
url: 'http://localhost:8080/api/upload/image'
}
}
})
.then(editor => {
console.log('编辑器初始化完成', editor);
})
.catch(error => {
console.error('初始化编辑器时发生错误', error);
});
</script>
</body>
</html>
关键配置说明
toolbar: 在工具栏中添加了'imageUpload',这样用户才能看到上传按钮。image.upload.url: 这是最关键的配置,它告诉 CKEditor 5,当用户点击上传按钮时,应该将文件发送到哪个 URL。请将其替换为你自己的后端接口地址。
第三步:处理跨域问题
如果你的前端页面(http://localhost:8081)和后端接口(http://localhost:8080)不在同一个端口下,就会遇到跨域问题,浏览器会阻止前端页面向后端接口发送请求。
解决方案:在后端添加 CORS 配置。
创建一个 CORS 配置类
package com.example.ckeditordemo.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
@Configuration
public class CorsConfig {
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
// 允许所有来源
config.addAllowedOrigin("*");
// 允许所有头信息
config.addAllowedHeader("*");
// 允许所有方法 (GET, POST, PUT, DELETE, OPTIONS 等)
config.addAllowedMethod("*");
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
}
或者在 Controller 方法上使用 @CrossOrigin 注解 (更简单)
如果你只想让某个特定的接口支持跨域,可以直接在 Controller 方法上添加注解,这是最简单的方式。
修改 FileUploadController.java:
// ... 其他 import ...
import org.springframework.web.bind.annotation.CrossOrigin;
@Slf4j
@RestController
@RequestMapping("/api/upload")
public class FileUploadController {
// ... @Autowired 和其他代码 ...
@PostMapping(value = "/image", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
// 在这里添加 @CrossOrigin 注解
// 允许来自 http://localhost:8081 的所有请求
// 或者直接使用 @CrossOrigin(origins = "*") 允许所有来源
@CrossOrigin(origins = "http://localhost:8081", allowCredentials = "true")
public ResponseEntity<?> uploadImage(@RequestParam("upload") MultipartFile file) {
// ... 上传逻辑不变 ...
}
}
注意: allowCredentials = "true" 通常用于处理包含 cookies 或认证头的请求,对于简单的文件上传,可以不加,如果遇到问题,可以尝试加上。
总结与运行
- 启动后端: 运行你的 Spring Boot 应用,确保服务器在
8080端口启动。 - 打开前端: 在浏览器中打开你的 HTML 文件(如果你使用一个简单的 Web 服务器,VS Code 的 Live Server 插件,它可能会在
8081或其他端口启动)。 - 测试:
- 在 CKEditor 中点击图片上传按钮。
- 选择一张本地图片。
- 点击“上传”。
- 观察控制台,如果成功,图片应该会直接插入到编辑器中,
src属性指向你后端返回的 URL (如http://localhost:8080/uploads/xxxx.jpg)。
至此,你已经成功地在 Java 项目中集成了 CKEditor 5 并实现了图片上传功能,这个方案是功能完整且可扩展的,你可以根据需要进一步优化,比如将图片保存到云存储(如阿里云OSS、AWS S3)等。
