下面我将提供一个完整、详细的步骤和代码示例,包括后端 Java 代码和前端 KindEditor 的配置。

核心流程
- 前端配置: 在 KindEditor 的初始化配置中,指定
imageUploadJson选项,指向你后端处理上传的 URL。 - 用户操作: 用户在 KindEditor 编辑器中点击“图片”按钮,选择“上传图片”,然后在弹出的对话框中选择本地图片文件并点击“上传”。
- 发送请求: 浏览器会自动向
imageUploadJson指定的 URL 发送一个包含文件数据的 POST 请求。 - 后端处理:
- 接收并解析这个 multipart 请求。
- 获取上传的文件对象。
- 对文件进行校验(如文件类型、大小)。
- 生成一个唯一的文件名,防止文件名冲突。
- 将文件保存到服务器上的某个目录(
upload/images)。 - 构造并返回一个符合 KindEditor 期望的 JSON 响应。
- 前端响应: KindEditor 接收到 JSON 响应后,如果上传成功,会将图片插入到编辑器中。
第一步:后端 Java 实现 (使用 Servlet API)
这里我们使用原生的 Java Servlet API 来实现,因为它不依赖任何第三方框架,更容易理解核心逻辑,如果你使用的是 Spring Boot,原理完全一样,只是接收请求的方式会更简单(例如用 @RequestParam("imgFile") MultipartFile file)。
创建 Maven 项目
确保你的项目是一个 Web 项目(Dynamic Web Project in Eclipse 或 Maven Web App in IntelliJ IDEA)。
添加依赖 (可选,但推荐)
虽然可以用 Servlet API 原生方式,但使用 Apache Commons FileUpload 可以极大简化文件解析的代码。
在 pom.xml 中添加依赖:

<dependencies>
<!-- Servlet API -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<!-- Apache Commons FileUpload -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
<!-- Commons IO 是 FileUpload 的一个可选依赖,但很好用 -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.11.0</version>
</dependency>
</dependencies>
编写上传 Servlet
创建一个名为 ImageUploadServlet.java 的 Servlet。
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
import java.util.UUID;
@WebServlet("/api/image/upload") // KindEditor 将会请求这个 URL
public class ImageUploadServlet extends HttpServlet {
// 上传文件存储目录
private static final String UPLOAD_DIR = "upload/images/";
// 允许上传的文件类型
private static final String[] ALLOWED_TYPES = {
"image/jpeg",
"image/png",
"image/gif",
"image/bmp"
};
// 允许上传的文件大小 (5MB)
private static final long MAX_FILE_SIZE = 5 * 1024 * 1024;
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 1. 检查是否为 multipart/form-data 请求
if (!ServletFileUpload.isMultipartContent(request)) {
// 不是文件上传请求,返回错误
printJsonResponse(response, -1, "请求类型错误,请使用 multipart/form-data");
return;
}
// 2. 配置上传参数
DiskFileItemFactory factory = new DiskFileItemFactory();
// 设置内存中存储文件大小的阈值,超过则写入临时文件
factory.setSizeThreshold(1024 * 1024); // 1 MB
// 设置临时文件存储目录
File repository = (File) getServletContext().getAttribute("javax.servlet.context.tempdir");
factory.setRepository(repository);
ServletFileUpload upload = new ServletFileUpload(factory);
// 设置单个文件最大大小
upload.setFileSizeMax(MAX_FILE_SIZE);
// 设置总请求最大大小
upload.setSizeMax(MAX_FILE_SIZE);
// 3. 处理上传的文件
try {
List<FileItem> formItems = upload.parseRequest(request);
if (formItems == null || formItems.isEmpty()) {
printJsonResponse(response, -1, "没有选择文件");
return;
}
for (FileItem item : formItems) {
// 跳过非文件域
if (!item.isFormField()) {
String fileName = new File(item.getName()).getName();
String contentType = item.getContentType();
long size = item.getSize();
// 4. 校验文件类型
boolean isAllowed = false;
for (String allowedType : ALLOWED_TYPES) {
if (allowedType.equalsIgnoreCase(contentType)) {
isAllowed = true;
break;
}
}
if (!isAllowed) {
printJsonResponse(response, -1, "不支持的文件类型: " + contentType);
return;
}
// 5. 校验文件大小
if (size > MAX_FILE_SIZE) {
printJsonResponse(response, -1, "文件大小超过限制 (最大 5MB)");
return;
}
// 6. 保存文件
// 确保上传目录存在
String uploadPath = getServletContext().getRealPath("") + File.separator + UPLOAD_DIR;
File uploadDir = new File(uploadPath);
if (!uploadDir.exists()) {
uploadDir.mkdirs();
}
// 生成唯一文件名,防止覆盖
String fileExtension = fileName.substring(fileName.lastIndexOf("."));
String uniqueFileName = UUID.randomUUID().toString() + fileExtension;
File storeFile = new File(uploadPath + File.separator + uniqueFileName);
item.write(storeFile);
// 7. 返回成功响应
// KindEditor 期望的 JSON 格式: {"error": 0, "url": "图片访问URL"}
String imageUrl = request.getContextPath() + "/" + UPLOAD_DIR + uniqueFileName;
printJsonResponse(response, 0, imageUrl);
}
}
} catch (Exception ex) {
ex.printStackTrace();
printJsonResponse(response, -1, "服务器内部错误: " + ex.getMessage());
}
}
/**
* 辅助方法:向响应中写入 JSON 数据
* @param response HttpServletResponse 对象
* @param error 错误码,0 表示成功,非 0 表示失败
* @param message 错误信息或图片 URL
*/
private void printJsonResponse(HttpServletResponse response, int error, String message) throws IOException {
response.setContentType("application/json; charset=UTF-8");
response.setCharacterEncoding("UTF-8");
PrintWriter out = response.getWriter();
// 使用 StringBuilder 构建 JSON 字符串
StringBuilder json = new StringBuilder();
json.append("{");
json.append("\"error\": ").append(error).append(",");
if (error == 0) {
json.append("\"url\": \"").append(message).append("\"");
} else {
json.append("\"message\": \"").append(message).append("\"");
}
json.append("}");
out.print(json.toString());
out.flush();
}
}
配置 web.xml
如果你的项目是 Servlet 3.0+,并且使用了 @WebServlet 注解,web.xml 不是必须的,但如果需要配置欢迎页面或其他过滤器,可以保留。
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<display-name>KindEditorUploadDemo</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
</web-app>
创建上传目录
在项目的 WebContent (或 webapp) 目录下,手动创建 upload/images 文件夹,这个
