Ueditor更改上传路径及自动删除不需要的图片

Ueditor更改上传路径及自动删除不需要的图片

最近项目需要用到富文本编辑,但是Ueditor默认图片保存在项目路径下,而且图片上传成功后不能获取到上传图片的路径(可能是本人未发现),这样就不好定位上传的图片做后期的删除操作,查阅资料拿了很多大神的代码成功后总结一下。
步骤如下

  1. 下载相应源码
  2. 更改源码,更改上传路径
  3. 做后端删除处理

直接在项目创建相应的包并用自己的代码覆盖即可。
Ueditor更改上传路径及自动删除不需要的图片

config.json
Ueditor更改上传路径及自动删除不需要的图片

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剪切路径的地方