CKEditor 5图片的上传方式
图片贴入正文支持的方式
- 从剪切板贴入
- 直接从文件系统中拖拽贴入
- 通过图片按钮选择文件贴入
插入图片后的工作过程
- 用户插入图片
- 编辑器使用一个暂时的图片占住该位置
- 编辑器向服务器上传该图片
- 图片上传完成,服务器返回该图片在服务器的url路径
- 编辑器从服务器下载该图片,替换掉暂时的图片
- 图片插入完成
编辑器上传文件的方式
主要有如下两种:
- 将图片上传到CKEditor提供的云服务上
- 将图片上传到我们自己的提供的服务器上
使用CKEditor Cloud Service
- 按照官方教程,注册一个云账号
- 创建CKEditor时加上如下配置,其中的url是注册账号后得到的
- 这样正文中的图片就保存在CKEditor的服务上,要拿到该图片时得保证能够连上服务器
ClassicEditor
.create( document.querySelector( '#editor' ), {
cloudServices: {
tokenUrl: 'https://example.com/cs-token-endpoint',
uploadUrl: 'https://your-organization-id.cke-cs.com/easyimage/upload/'
}
} )
.then( ... )
.catch( ... );
上传到自定义服务器 - 前端配置
上传到自定义服务器在前端需要做的工作实在是非常少了,只需要按照如下配置即可,指定自定义服务器的地址。
ClassicEditor
.create(document.querySelector("#editor"), {
toolbar: ["heading", "|", "alignment:left", "alignment:center", "alignment:right", "alignment:adjust", "|", "bold", "italic", "blockQuote", "link", "|", "bulletedList", "numberedList", "imageUpload", "|", "undo", "redo"],
ckfinder: {
uploadUrl: "http://localhost:8090/Floyd/attachment/uploadImage.do"
}
})
.then(editor => {
myEditor = editor;
})
.catch(error => {
console.error(error);
});
编辑器会自动进行上传,其需要服务器返回一个JSON数组,包含两个字段:uploaded(true / false)和url(相对路径)。举个例子如下。若没有返回内容或返回内容uploaded不为true,则会出现上传失败的情况。
["uploaded":"true", "url":"image-path..."]
上传到自定义服务器 - 后端配置(SpringMVC接收)
我是按照如下逻辑进行接收的
- 在Spring配置中指定文件接收的Resolver(采用注解方式进行配置就是在配置类中声明一个Bean)
@Bean
public MultipartResolver multipartResolver() throws IOException {
CommonsMultipartResolver resolver = new CommonsMultipartResolver();
resolver.setUploadTempDir(new FileSystemResource("temp/"));
return resolver;
}
- Controller中使用@RequestPart(“upload”)注解直接将接收到的文件转换为MultipartFile对象。CKEditor上传图片的parameter name为”upload”,这是我使用浏览器抓包抓到的。
@RequestMapping("/uploadImage.do")
@ResponseBody
public Map<String, String> receiveImage(@RequestPart("upload") MultipartFile file, HttpServletRequest request) {
return attachmentService.ckEditorUploadImage(file, request);
}
- 对MultipartFile进行处理,直接调用其transferTo()将该文件转存到指定文件中。再将该文件的服务器路径返回即可
@Service("attachmentService")
public class AttachmentServiceImpl implements AttachmentService{
private static final String CK_IMAGE_PATH = File.separator + "uploadImage";
public Map<String, String> ckEditorUploadImage(MultipartFile file, HttpServletRequest request) {
if(file==null || "".equals(file.getOriginalFilename().trim())) {
return generateResult(false, "#");
}
String originalName = file.getOriginalFilename();
// generate file name
String localFileName = System.currentTimeMillis() + "-" + originalName;
// get project path
String projectRealPath = request.getSession().getServletContext().getRealPath("");
// get the real path to store received images
String realPath = projectRealPath + CK_IMAGE_PATH;
File imageDir = new File(realPath);
if(!imageDir.exists()) {
imageDir.mkdirs();
}
String localFilePath = realPath + File.separator + localFileName;
try {
file.transferTo(new File(localFilePath));
} catch (IllegalStateException e) {
e.printStackTrace();
// log here
} catch (IOException e) {
e.printStackTrace();
// log here
}
String imageContextPath = request.getContextPath() + "/uploadImage" + "/" + localFileName;
// log here +
System.out.println("received file original name: " + originalName);
System.out.println("stored local file name: " + localFileName);
System.out.println("file stored path: " + localFilePath);
System.out.println("returned url: " + imageContextPath);
// log here -
return generateResult(true, imageContextPath);
}
private Map<String, String> generateResult(boolean uploaded, String relativeUrl){
Map<String, String> result = new HashMap<String, String>();
result.put("uploaded", uploaded + "");
result.put("url", relativeUrl);
return result;
}
}
上传到自定义服务器 - 后端配置(普通方式接收)
采用commons-fileupload包提供的方式进行接收,(这里提供:官方指导手册, 我整理的简易教程)。
如下代码中需要注意的是,获取的FileItem中,只对FieldName为”upload”的进行解析,其余item并不是我们需要的数据,如图所示,Request Payload下的三段数据分别被解析成三个FileItem,很显然我们只需要第一个即可。
public Map<String, String> ckEditorUploadImage(HttpServletRequest request) {
// judge if the request is multi part
if(ServletFileUpload.isMultipartContent(request)) {
// create a factory for disk-based file items
DiskFileItemFactory factory = new DiskFileItemFactory();
// get the servlet default temp dir, and set it as multi part temp dir
ServletContext context = request.getServletContext();
File tempDir = (File) context.getAttribute("javax.servlet.context.tempdir");
factory.setRepository(tempDir);
// set size threshold, if multipart file is larger than the threshold, it will be
// stored in temp dir
factory.setSizeThreshold(100000000);
// create a new file upload handler
ServletFileUpload upload = new ServletFileUpload(factory);
// set single file size limit: 10MB
upload.setFileSizeMax(1024*1024*10);
// set whole request size limit: 100MB
upload.setSizeMax(1024*1024*100);
// parse the request
List<FileItem> items = null;
try {
items = upload.parseRequest(request);
} catch (FileUploadException e) {
e.printStackTrace();
// log here
}
// processing File Item
Iterator<FileItem> iter = items.iterator();
String localFileName = null;
while(iter.hasNext()) {
FileItem item = iter.next();
if(!"upload".equals(item.getFieldName())) {
continue;
}
String originalName = item.getName();
localFileName = System.currentTimeMillis() + "-" + originalName;
String localFilePath = context.getRealPath(CK_IMAGE_PATH) + File.separator + localFileName;
File localFile = new File(localFilePath);
try {
if(!localFile.getParentFile().exists()) {
localFile.getParentFile().mkdirs();
}
if(!localFile.exists()) {
localFile.createNewFile();
}
item.write(localFile);
} catch (IOException e) {
e.printStackTrace();
// log here
} catch (Exception e) {
e.printStackTrace();
// log here
}
}
String imageContextPath = request.getContextPath() + CK_IMAGE_PATH + File.separator + localFileName;
// log here +
System.out.println("stored local file name: " + localFileName);
System.out.println("returned url: " + imageContextPath);
// log here -
return generateResult(true, imageContextPath);
}
return generateResult(false, "/");
}