先描述一下个人经历吧,用过的富文本编辑器也有几个,图片处理这块,总结一下,我遇到的有两种,一种是选择了图片,富文本显示的是图片文件的src的属性是base64编码,一种是,选择了图片之后直接上传到服务器,这时,富文本显示的是图片文件的src属性是服务器的地址。
刚开始做项目的时候用的是百度editor,当时觉得复杂,觉得能实现图片上传就是成功,他的方式就是第二种,当你选择了图片,图片直接被上传到服务器,但这种方式有个不好的地方,就是当你上传一张图片成功后,按了一下退格,那会怎么样,浏览器不显示图片了,但服务器上还有。但用户不知情,会导致服务器垃圾文件越来越多,当用户重新编辑文章的时候,删除图片,也不会删除服务器的图片(当然,开发者最清楚这件事了!),一会说解决办法。
后来,我接触了base64编码的方式,就是第一种。我是这样想的,用户选择一张图片,是js处理之后,将图片的src路径改为了大长串的base64码,(在用户眼里,图片也是正常显示),但不会上传到后台服务器,这貌似很又好了,对服务器来说。当用户点击提交的时候,富文本中包含大量的base64码的图片,(刚开始想直接放进数据库,但考虑到数据库可能导致存储空间紧张),服务器用正则表达式解析富文本提交过来的文章内容,然后编码成图片文件,然后将文章内容中图片的src改成服务器地址,这样能省数据库空间。但是,当用户编辑文章的时候就麻烦了,用户编辑文章,图片显示的还是服务器地址,用户在富文本内,删除图片,服务器还是没有删除,当用户重新选择图片,又是base64码,当点击提交的时候,服务器解析图片文件的src的时候要分开处理,如果是base64的,就转成文件,如果不是则不作处理。但还是没解决用户重新编辑文章删除图片的问题,一会说解决办法。(后来才发现,这样上传方式凸显了我的天真和青涩)。
先处理第一种和第二种上传文章的编辑的时候服务器的删除问题吧,(解决思路是,找到用户在编辑的时候删除的图片,也让服务器删除),当我点击编辑文章的时候,我会把回显回来的内容进行js解析,这两种图片的回显都是服务器地址哦!js怎么解析呢,正则表达式匹配所有图片,生成一个数组,旧图片数组,当用户编辑完成图片的时候也渠道富文本的内容,也生成一个数组,新图片数组。旧图片数组全是,服务器地址的图片路径,新图片数组,只收集全是服务器地址的图片路径,用户新上传的图片是base64的,需要服务器保存的。这样两个数组进行对比,可以找出用户在富文本中删除了那几张图片。把这个需要删除的图片数组传给后台,后台删除图片即可。这样确实解决的编辑的时候的图片删除问题。
base64上传还产生了新问题(我发现,使用base64上传图片,这个base64码即使使用canvas做一下压缩,20来张图片的话,富文本内容长度就会数十万,根本无法提交到后台,真以为post请求可以无限长度呢),于是base64这个方案破灭了,又重写了base64上传的方法,把图片扔到服务器,在回显服务的路径。又和第一种方案一样了。
用户的编辑文章解决了服务器的垃圾图片问题。正准备开心的时候,突然想到,如果用户添加文章的时候,选择了几张图片,这时图片已经到了服务器上,但用户重新刷新了页面怎么办,或者说用户不想写文章,睡觉去了怎么办。
新问题就有新方案!
解决思路,用户每上传一张图片,就把图片存放的路径扔redis里面,设置超时时间,比如预设,用户在8小时之内一定会将编写的文章提交了,所以redis设置key就是图片路径,value随意。redis有发布订阅机制,可以在key超时时,将过期的key返回推送给java程序,这样处理就很好了。如果用户选择了图片,上传到服务器了,没有点击提交,过期了之后程序还是会删除服务器上的图片,如果上传了,那就用程序删除redis里的key,那样不会触发key的超时推送。这就就解决了用户上传一堆图,但是没有提交文章的问题。
具体配置如下:
修改redis.conf设置key超时提醒
在大概1024行这样设置,版本不同可能位置不同。
在springboot设置redis订阅的配置文件:
@Bean //相当于xml中的bean
public RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory,
MessageListenerAdapter listenerAdapter) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
//订阅了一个叫具体的 的通道
container.addMessageListener(listenerAdapter, new PatternTopic("__key*@0__:expired"));
//这个container 可以添加多个 messageListener
return container;
}
/**
* 消息监听器适配器,绑定消息处理器,利用反射技术调用消息处理器的业务方法
* @param receiver
* @return
*/
@Bean
MessageListenerAdapter listenerAdapter(ReceiverService receiver) {
/*这个地方 是给messageListenerAdapter 传入一个消息接受的处理器,利用反射的方法调用“receiveMessage”
也有好几个重载方法,这边默认调用处理器的方法 叫handleMessage 可以自己到源码里面看
这里实质就是监听调用ReceiverService类中的receiveMessage方法
*/
return new MessageListenerAdapter(receiver, "receiveMessage");
}
这是处理信息的类:
package com.xinxiera.config;
import java.io.File;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class ReceiverService {
@Value("${windows.path}")
private String windowsPath;
@Value("${linux.path}")
private String linuxPath;
public void receiveMessage(String key) {
//订阅了redis的Key超时,会返回redis删除的key,服务器需要删除图片
if(key.startsWith("uploadedImg")) {
String path = null;
String os = System.getProperty("os.name");
if(os.toLowerCase().startsWith("win")){
path = windowsPath;
}else {
path = linuxPath;
}
File file = new File(path+key.substring(12));
if(file.exists()) {
file.delete();
}
}
}
}
这样即使用户上传了图片,关了浏览器,图片也会被服务器删除;