Ueditor更改上传路径及自动删除不需要的图片
Ueditor更改上传路径及自动删除不需要的图片
最近项目需要用到富文本编辑,但是Ueditor默认图片保存在项目路径下,而且图片上传成功后不能获取到上传图片的路径(可能是本人未发现),这样就不好定位上传的图片做后期的删除操作,查阅资料拿了很多大神的代码成功后总结一下。
步骤如下
- 下载相应源码
- 更改源码,更改上传路径
- 做后端删除处理
直接在项目创建相应的包并用自己的代码覆盖即可。
config.json
controller.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
import="com.baidu.ueditor.ActionEnter"
pageEncoding="UTF-8"%>
<%@ page trimDirectiveWhitespaces="true" %>
<%
request.setCharacterEncoding( "utf-8" );
response.setHeader("Content-Type" , "text/html");
String rootPath = application.getRealPath( "/" );
String saveRootPath = "D:/test/";
out.write( new ActionEnter( request, rootPath,saveRootPath).exec() );
%>
ActionEnter.java
package com.baidu.ueditor;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import com.baidu.ueditor.define.ActionMap;
import com.baidu.ueditor.define.AppInfo;
import com.baidu.ueditor.define.BaseState;
import com.baidu.ueditor.define.State;
import com.baidu.ueditor.hunter.FileManager;
import com.baidu.ueditor.hunter.ImageHunter;
import com.baidu.ueditor.upload.Uploader;
public class ActionEnter {
private HttpServletRequest request = null;
private String rootPath = null;
private String contextPath = null;
private String actionType = null;
private ConfigManager configManager = null;
private String saveRootPath = null;
public ActionEnter ( HttpServletRequest request, String rootPath , String saveRootPath) {
this.request = request;
this.rootPath = rootPath;
this.saveRootPath = saveRootPath;
this.actionType = request.getParameter( "action" );
this.contextPath = request.getContextPath();
this.configManager = ConfigManager.getInstance(this.rootPath, this.contextPath, this.saveRootPath, request.getRequestURI());
}
public String exec () {
String callbackName = this.request.getParameter("callback");
if ( callbackName != null ) {
if ( !validCallbackName( callbackName ) ) {
return new BaseState( false, AppInfo.ILLEGAL ).toJSONString();
}
return callbackName+"("+this.invoke()+");";
} else {
return this.invoke();
}
}
public String invoke() {
if ( actionType == null || !ActionMap.mapping.containsKey( actionType ) ) {
return new BaseState( false, AppInfo.INVALID_ACTION ).toJSONString();
}
if ( this.configManager == null || !this.configManager.valid() ) {
return new BaseState( false, AppInfo.CONFIG_ERROR ).toJSONString();
}
State state = null;
int actionCode = ActionMap.getType( this.actionType );
Map<String, Object> conf = null;
switch ( actionCode ) {
case ActionMap.CONFIG:
return this.configManager.getAllConfig().toString();
case ActionMap.UPLOAD_IMAGE:
case ActionMap.UPLOAD_SCRAWL:
case ActionMap.UPLOAD_VIDEO:
case ActionMap.UPLOAD_FILE:
conf = this.configManager.getConfig( actionCode );
state = new Uploader( request, conf ).doExec();
break;
case ActionMap.CATCH_IMAGE:
conf = configManager.getConfig( actionCode );
String[] list = this.request.getParameterValues( (String)conf.get( "fieldName" ) );
state = new ImageHunter( conf ).capture( list );
break;
case ActionMap.LIST_IMAGE:
case ActionMap.LIST_FILE:
conf = configManager.getConfig( actionCode );
int start = this.getStartIndex();
state = new FileManager( conf ).listFile( start );
break;
}
return state.toJSONString();
}
public int getStartIndex () {
String start = this.request.getParameter( "start" );
try {
return Integer.parseInt( start );
} catch ( Exception e ) {
return 0;
}
}
/**
* callback参数验证
*/
public boolean validCallbackName ( String name ) {
if ( name.matches( "^[a-zA-Z_]+[\\w0-9_]*$" ) ) {
return true;
}
return false;
}
}
ConfigManager.java
package com.baidu.ueditor;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Map;
import org.json.JSONArray;
import org.json.JSONObject;
import com.baidu.ueditor.define.ActionMap;
public final class ConfigManager {
private final String rootPath;
private final String originalPath;
private final String contextPath;
private static final String configFileName = "config.json";
private String parentPath = null;
private String saveRootPath = null;// -------------------------
private JSONObject jsonConfig = null;
// 涂鸦上传filename定义
private final static String SCRAWL_FILE_NAME = "scrawl";
// 远程图片抓取filename定义
private final static String REMOTE_FILE_NAME = "remote";
private ConfigManager(String rootPath, String contextPath, String saveRootPath, String uri) throws FileNotFoundException, IOException {
rootPath = rootPath.replace("\\", "/");
this.saveRootPath = saveRootPath;
this.rootPath = rootPath;
this.contextPath = contextPath;
if (contextPath.length() > 0) {
this.originalPath = this.rootPath + uri.substring(contextPath.length());
} else {
this.originalPath = this.rootPath + uri;
}
this.initEnv();
}
/*
* 通过一个给定的路径构建一个配置管理器, 该管理器要求地址路径所在目录下必须存在config.properties文件
*/
private ConfigManager ( String rootPath, String contextPath, String uri ) throws FileNotFoundException, IOException {
rootPath = rootPath.replace( "\\", "/" );
this.rootPath = rootPath;
this.contextPath = contextPath;
if ( contextPath.length() > 0 ) {
this.originalPath = this.rootPath + uri.substring( contextPath.length() );
} else {
this.originalPath = this.rootPath + uri;
}
this.initEnv();
}
/**
* 配置管理器构造工厂
* @param rootPath 服务器根路径
* @param contextPath 服务器所在项目路径
* @param uri 当前访问的uri
* @return 配置管理器实例或者null
*/
public static ConfigManager getInstance(String rootPath, String contextPath, String saveRootPath, String uri) {
try {
return new ConfigManager(rootPath, contextPath, saveRootPath, uri);
} catch (Exception var4) {
return null;
}
}
// 验证配置文件加载是否正确
public boolean valid () {
return this.jsonConfig != null;
}
public JSONObject getAllConfig () {
return this.jsonConfig;
}
public Map<String, Object> getConfig ( int type ) {
Map<String, Object> conf = new HashMap<String, Object>();
String savePath = null;
switch ( type ) {
case ActionMap.UPLOAD_FILE:
conf.put( "isBase64", "false" );
conf.put( "maxSize", this.jsonConfig.getLong( "fileMaxSize" ) );
conf.put( "allowFiles", this.getArray( "fileAllowFiles" ) );
conf.put( "fieldName", this.jsonConfig.getString( "fileFieldName" ) );
savePath = this.jsonConfig.getString( "filePathFormat" );
break;
case ActionMap.UPLOAD_IMAGE:
conf.put( "isBase64", "false" );
conf.put( "maxSize", this.jsonConfig.getLong( "imageMaxSize" ) );
conf.put( "allowFiles", this.getArray( "imageAllowFiles" ) );
conf.put( "fieldName", this.jsonConfig.getString( "imageFieldName" ) );
savePath = this.jsonConfig.getString( "imagePathFormat" );
break;
case ActionMap.UPLOAD_VIDEO:
conf.put( "maxSize", this.jsonConfig.getLong( "videoMaxSize" ) );
conf.put( "allowFiles", this.getArray( "videoAllowFiles" ) );
conf.put( "fieldName", this.jsonConfig.getString( "videoFieldName" ) );
savePath = this.jsonConfig.getString( "videoPathFormat" );
break;
case ActionMap.UPLOAD_SCRAWL:
conf.put( "filename", ConfigManager.SCRAWL_FILE_NAME );
conf.put( "maxSize", this.jsonConfig.getLong( "scrawlMaxSize" ) );
conf.put( "fieldName", this.jsonConfig.getString( "scrawlFieldName" ) );
conf.put( "isBase64", "true" );
savePath = this.jsonConfig.getString( "scrawlPathFormat" );
break;
case ActionMap.CATCH_IMAGE:
conf.put( "filename", ConfigManager.REMOTE_FILE_NAME );
conf.put( "filter", this.getArray( "catcherLocalDomain" ) );
conf.put( "maxSize", this.jsonConfig.getLong( "catcherMaxSize" ) );
conf.put( "allowFiles", this.getArray( "catcherAllowFiles" ) );
conf.put( "fieldName", this.jsonConfig.getString( "catcherFieldName" ) + "[]" );
savePath = this.jsonConfig.getString( "catcherPathFormat" );
break;
case ActionMap.LIST_IMAGE:
conf.put( "allowFiles", this.getArray( "imageManagerAllowFiles" ) );
conf.put( "dir", this.jsonConfig.getString( "imageManagerListPath" ) );
conf.put( "count", this.jsonConfig.getInt( "imageManagerListSize" ) );
break;
case ActionMap.LIST_FILE:
conf.put( "allowFiles", this.getArray( "fileManagerAllowFiles" ) );
conf.put( "dir", this.jsonConfig.getString( "fileManagerListPath" ) );
conf.put( "count", this.jsonConfig.getInt( "fileManagerListSize" ) );
break;
}
conf.put( "savePath", savePath );
conf.put( "rootPath", this.rootPath );
conf.put("saveRootPath", this.saveRootPath);
return conf;
}
private void initEnv () throws FileNotFoundException, IOException {
File file = new File( this.originalPath );
if ( !file.isAbsolute() ) {
file = new File( file.getAbsolutePath() );
}
this.parentPath = file.getParent();
String configContent = this.readFile( this.getConfigPath() );
try{
JSONObject jsonConfig = new JSONObject( configContent );
this.jsonConfig = jsonConfig;
} catch ( Exception e ) {
this.jsonConfig = null;
}
}
private String getConfigPath () {
return this.parentPath + File.separator + ConfigManager.configFileName;
}
private String[] getArray ( String key ) {
JSONArray jsonArray = this.jsonConfig.getJSONArray( key );
String[] result = new String[ jsonArray.length() ];
for ( int i = 0, len = jsonArray.length(); i < len; i++ ) {
result[i] = jsonArray.getString( i );
}
return result;
}
private String readFile ( String path ) throws IOException {
StringBuilder builder = new StringBuilder();
try {
InputStreamReader reader = new InputStreamReader( new FileInputStream( path ), "UTF-8" );
BufferedReader bfReader = new BufferedReader( reader );
String tmpContent = null;
while ( ( tmpContent = bfReader.readLine() ) != null ) {
builder.append( tmpContent );
}
bfReader.close();
} catch ( UnsupportedEncodingException e ) {
// 忽略
}
return this.filter( builder.toString() );
}
// 过滤输入字符串, 剔除多行注释以及替换掉反斜杠
private String filter ( String input ) {
return input.replaceAll( "/\\*[\\s\\S]*?\\*/", "" );
}
}
BinaryUploader.java
package com.baidu.ueditor.upload;
import com.baidu.ueditor.PathFormat;
import com.baidu.ueditor.define.AppInfo;
import com.baidu.ueditor.define.BaseState;
import com.baidu.ueditor.define.FileType;
import com.baidu.ueditor.define.State;
import app.common.ClearFiles;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.fileupload.FileItemIterator;
import org.apache.commons.fileupload.FileItemStream;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
public class BinaryUploader {
public BinaryUploader() {
}
public static final State save(HttpServletRequest request,
Map<String, Object> conf) {
FileItemStream fileStream = null;
boolean isAjaxUpload = request.getHeader( "X_Requested_With" ) != null;
if (!ServletFileUpload.isMultipartContent(request)) {
return new BaseState(false, AppInfo.NOT_MULTIPART_CONTENT);
}
ServletFileUpload upload = new ServletFileUpload(
new DiskFileItemFactory());
if ( isAjaxUpload ) {
upload.setHeaderEncoding( "UTF-8" );
}
try {
FileItemIterator iterator = upload.getItemIterator(request);
while (iterator.hasNext()) {
fileStream = iterator.next();
if (!fileStream.isFormField())
break;
fileStream = null;
}
if (fileStream == null) {
return new BaseState(false, AppInfo.NOTFOUND_UPLOAD_DATA);
}
String savePath = (String) conf.get("savePath");
String originFileName = fileStream.getName();
String suffix = FileType.getSuffixByFilename(originFileName);
originFileName = originFileName.substring(0,
originFileName.length() - suffix.length());
savePath = savePath + suffix;
long maxSize = ((Long) conf.get("maxSize")).longValue();
if (!validType(suffix, (String[]) conf.get("allowFiles"))) {
return new BaseState(false, AppInfo.NOT_ALLOW_FILE_TYPE);
}
savePath = PathFormat.parse(savePath, originFileName);
String physicalPath = (String) conf.get("saveRootPath") + savePath;
String sessionid = request.getSession().getId();
ClearFiles.getCealrFiles().addRmUeditor(sessionid, physicalPath);
这个是删除多余图片工具类,每次上传图片便将路径传过去,提交的时候比对并删除
InputStream is = fileStream.openStream();
State storageState = StorageManager.saveFileByInputStream(is,
physicalPath, maxSize);
is.close();
if (storageState.isSuccess()) {
storageState.putInfo("url", PathFormat.format(savePath));
storageState.putInfo("type", suffix);
storageState.putInfo("original", originFileName + suffix);
}
return storageState;
} catch (FileUploadException e) {
return new BaseState(false, AppInfo.PARSE_REQUEST_ERROR);
} catch (IOException e) {
}
return new BaseState(false, AppInfo.IO_ERROR);
}
private static boolean validType(String type, String[] allowTypes) {
List<String> list = Arrays.asList(allowTypes);
return list.contains(type);
}
}
ImageUtil.java
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class ImageUtil {
public static List<String> getImageSrc(String htmlCode,boolean cut) {
List<String> imageSrcList = new ArrayList<String>();
Pattern p = Pattern.compile("<img\\b[^>]*\\bsrc\\b\\s*=\\s*('|\")?([^'\"\n\r\f>]+(\\.jpg|\\.bmp|\\.eps|\\.gif|\\.mif|\\.miff|\\.png|\\.tif|\\.tiff|\\.svg|\\.wmf|\\.jpe|\\.jpeg|\\.dib|\\.ico|\\.tga|\\.cut|\\.pic)\\b)[^>]*>", Pattern.CASE_INSENSITIVE);
Matcher m = p.matcher(htmlCode);
String quote = null;
String src = null;
while (m.find()) {
quote = m.group(1);
String pathsrc = (quote == null || quote.trim().length() == 0) ? m.group(2).split("\\s+")[0] : m.group(2);
src = cut?pathsrc.substring(pathsrc.lastIndexOf("/")+1,pathsrc.lastIndexOf(".")):pathsrc;
imageSrcList.add(src);
}
return imageSrcList;
}
}
ClearFiles.java
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ClearFiles implements Runnable{
private final static Logger logger = LoggerFactory.getLogger(ClearFiles.class);
private ArrayList<String> clearPaths = new ArrayList<>(); //所有待删除路径
private Map<String,Map<String,String>> ueditor_map = new HashMap<>(); //ueditor上传记录
private List<Map<String,String>> ueditorcontent = new ArrayList<>(); //ueditor待处理内容
private static ClearFiles clearFiles= null;
private Object lock1 = new Object();
public static synchronized ClearFiles getCealrFiles() {
if(clearFiles == null) {
clearFiles = new ClearFiles();
Thread th = new Thread(clearFiles);
th.start();
}
return clearFiles;
}
private void clearCACimg() {
if(clearPaths.size()>0) {
for(int i=0;i<clearPaths.size();i++) {
String path = clearPaths.remove(0);
File filerm = new File(path);
if(filerm.exists()) {
boolean rmre=filerm.delete();
if(rmre) {
logger.info("删除图片"+path+"成功");
}else {
logger.error("删除图片"+path+"失败");
}
}else {
logger.info("图片"+path+"不存在");
}
}
}
}
//线程操作开始处理待删除富文本内容
private void ForCONimg() {
synchronized (lock1) {
if(ueditorcontent.size()>0) {
for(int i=0;i<ueditorcontent.size();i++) {
Map<String,String> map = ueditorcontent.remove(0);
if(map.get("type").equals(Constant.DELETEOLD)) {
DoClearUedOld(map); //新旧富文本内容对比删除多余图片
}else {
AddUeContent(map.get("content"),map.get("type"),map.get("rootpath"),map.get("sessionid")); //删除内容或者新增内容删除多余图片
}
}
}
}
}
@Override
public void run() {
while(true) {
ForCONimg();
clearCACimg();
try {
Thread.sleep(1000*10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* 前端传入待删除的图片路径
* @param path
*/
public void addRmPath(String path,Boolean decode) {
try{
String filePath = decode?("D://test"+path.substring(path.indexOf("/upload"))):path;
clearPaths.add(filePath);
}catch (Exception e) {
logger.error("图片地址解析出错"+path);
}
}
//ueditor上传图片记录
public void addRmUeditor(String sessionid,String path) {
String key = path.substring(path.lastIndexOf("/")+1,path.lastIndexOf("."));
if(ueditor_map.containsKey(sessionid)) {
ueditor_map.get(sessionid).put(key, path);
}else {
Map<String,String> ssmap = new HashMap<>();
ssmap.put(key, path);
ueditor_map.put(sessionid, ssmap);
}
}
//提交触发准备删除当前session下未保存的图片
public void clearUedBysessionId(String sessionid,List<String> list ) {
if(ueditor_map.containsKey(sessionid)) {
Map<String,String> ssmap = ueditor_map.remove(sessionid);
for(String key:list) {
ssmap.remove(key);
}
if(ssmap.size()>0) {
Set<String> keys = ssmap.keySet();
for(String key:keys) {
clearPaths.add(ssmap.get(key));
}
ssmap.clear();
}
}
}
/**
*
* @param content 富文本内容
* @param type 操作类型(全部直接删除:DELETEALL;比对后删除多余:DELETEMORE)
* @param rootpath 根地址
*/
public void AddUeContent(String content,String type,String rootpath,String sessionid) {
if(type.equals(Constant.DELETEALL)) {
List<String> list = ImageUtil.getImageSrc(content,false);
for(String path:list) {
String clpath = rootpath+ path.substring(path.indexOf("ueditor/"));
addRmPath(clpath,false);
}
}else {
List<String> list = ImageUtil.getImageSrc(content,true);
clearUedBysessionId(sessionid,list);
}
}
//用于编辑后比较并删除没有用到的图片
private void DoClearUedOld(Map<String,String> map) {
String newcon = map.get("newcon");
String oldcon = map.get("oldcon");
String rootpath = map.get("rootpath");
List<String> oldconlist = ImageUtil.getImageSrc(oldcon,false);
List<String> onewconlist = ImageUtil.getImageSrc(newcon,false);
System.out.println("oldconlist"+oldconlist);
System.out.println("onewconlist"+onewconlist);
for(String path:oldconlist) {
boolean search = false;
for(String path2:onewconlist) {
if(path.equals(path2)) {
search = true;
break;
}
}
if(!search) {
String clpath = rootpath+ path.substring(path.indexOf("ueditor/"));
addRmPath(clpath,false);
}
}
}
/**提交的时候调用
**/
public void addContentMap(String sessionid,String content,String type,String rootpath) {
synchronized (lock1) {
Map<String,String> map =new HashMap<>();
map.put("content", content);
map.put("sessionid", sessionid);
map.put("type", type);
map.put("rootpath", rootpath);
ueditorcontent.add(map);
}
}
/**编辑富文本的时候调用传入两个富文本内容
**/
public void addNOContentMap(String newcon,String oldcon,String rootpath) {
synchronized (lock1) {
Map<String,String> map =new HashMap<>();
map.put("newcon", newcon);
map.put("oldcon", oldcon);
map.put("rootpath", rootpath);
map.put("type", Constant.DELETEOLD);
ueditorcontent.add(map);
}
}
}
删除的时候调用
//content是前端传过来的富文本内容,rootpath和配置一样,别忘了tomcat配置一下地址映射,将保存图片的路径可以访问
String rootpath="D:/test/";
ClearFiles.getCealrFiles().addContentMap(request.raw().getSession().getId(),content,Constant.DELETEALL,rootpath);
新增和编辑的时候调用
//更具是否有id判断新增还是编辑,调用不同的业务,content是新的富文本内容,oldcontent是老的需要从数据库全部查出来
ClearFiles.getCealrFiles().addContentMap(request.raw().getSession().getId(),content,Constant.DELETEMORE,rootpath);
if(id != -1) {
String oldcontent = contentSeervice.getContentById(String.valueOf(id));//自己的services业务查原来的内容
ClearFiles.getCealrFiles().addNOContentMap(content, oldcontent, rootpath);
}
最后总结一下容易出问题的地方
- 要配置tomcat映射,不然不能回显
- imageUrlPrefix就是tomcat映射的路径 前缀
<Context path="/XXXX/static" docBase="D:\test" reloadable="true" />
- 根据路径的不同需要自己更改substring剪切路径的地方