关于文章中的FTP上传没有附上代码,不好意思,因为我觉得FTP上传的类到处都是,而我用的是公司内部的FTP的API,不太方便暴露,所以就省略了~请见谅,请大家自行选择FTP上传类吧
最近在项目中遇到了使用文本编辑器的情况,但是根据项目要求,文本编辑器的图片不能保存在本地服务器上,必须上传到远程文件服务器上,找了众多资料,没有找到答案,最后终于自己反编译了百度的JAR包,完完整整的研究了一次逻辑,终于实现了该功能,这里分享一下.以免后来者枉费时间.
步骤1.在自己的工程中实现一个ActionEnter类
package com.xxxx.ueditor; import com.baidu.ueditor.ConfigManager; import com.baidu.ueditor.define.ActionMap; 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.xxxx.ueditor.upload.Uploader; import java.util.Map; import javax.servlet.http.HttpServletRequest; import org.apache.log4j.Logger; public class ActionEnter { //日志器 protected Logger log = Logger.getLogger(ActionEnter.class); private HttpServletRequest request = null; private String rootPath = null; private String contextPath = null; private String actionType = null; private ConfigManager configManager = null; public ActionEnter(HttpServletRequest request, String rootPath) { this.request = request; this.rootPath = rootPath; this.actionType = request.getParameter("action"); this.contextPath = request.getContextPath(); this.configManager = ConfigManager.getInstance(this.rootPath, this.contextPath, request.getRequestURI()); } public String exec() { String callbackName = this.request.getParameter("callback"); if (callbackName != null) { if (!validCallbackName(callbackName)) { return new BaseState(false, 401).toJSONString(); } return callbackName + "(" + invoke() + ");"; } String response = invoke(); log.debug(response); return response; } @SuppressWarnings({ "unchecked", "rawtypes" }) public String invoke() { if ((this.actionType == null) || (!ActionMap.mapping.containsKey(this.actionType))) { return new BaseState(false, 101).toJSONString(); } if ((this.configManager == null) || (!this.configManager.valid())) { return new BaseState(false, 102).toJSONString(); } State state = null; int actionCode = ActionMap.getType(this.actionType); Map conf = null; switch (actionCode) { case 0: return this.configManager.getAllConfig().toString(); case 1: case 2: case 3: case 4: conf = this.configManager.getConfig(actionCode); conf.put("useFtpUpload",this.configManager.getAllConfig().getString("useFtpUpload")); conf.put("keepLocalFile",this.configManager.getAllConfig().getString("keepLocalFile")); state = new Uploader(this.request, conf).doExec(); break; case 5: conf = this.configManager.getConfig(actionCode); String[] list = this.request.getParameterValues((String)conf.get("fieldName")); state = new ImageHunter(conf).capture(list); break; case 6: case 7: conf = this.configManager.getConfig(actionCode); int start = getStartIndex(); state = new FileManager(conf).listFile(start); } return state.toJSONString(); } public int getStartIndex() { String start = this.request.getParameter("start"); try { return Integer.parseInt(start); } catch (Exception e) { } return 0; } public boolean validCallbackName(String name) { if (name.matches("^[a-zA-Z_]+[\\w0-9_]*$")) { return true; } return false; } }
这个类是反编译的原有百度的代码,只加入了两行读取配置文件的内容:
conf.put("useFtpUpload",this.configManager.getAllConfig().getString("useFtpUpload")); //是否使用FTP上传
conf.put("keepLocalFile",this.configManager.getAllConfig().getString("keepLocalFile")); //是否上传后保留本地服务器文件
步骤2.在ueditor目录下的jsp目录下,找到controller.jsp,修改ActionEnter的实现
<%@ page language="java" contentType="text/html; charset=UTF-8" import="com.xxxx.ueditor.ActionEnter" pageEncoding="UTF-8"%> <% request.setCharacterEncoding( "utf-8" ); response.setHeader("Content-Type" , "text/html"); String rootPath = application.getRealPath( "/" ); out.write( new ActionEnter( request, rootPath ).exec() ); %>
只修改一处import改为刚才创建的ActionEnter即可
步骤3.修改controller.jsp同级目录下的config.json,加入之前新增的两个配置项,并且修改prefix为文件服务器的域名
/* 前后端通信相关的配置,注释只允许使用多行方式 */ { "useFtpUpload": "true", /* 是否使用FTP上传 */ "keepLocalFile": "true", /* 使用FTP上传后本地服务器是否保存 */ /* 上传图片配置项 */ "imageActionName": "uploadimage", /* 执行上传图片的action名称 */ "imageFieldName": "upfile", /* 提交的图片表单名称 */ "imageMaxSize": 3145728, /* 上传大小限制,单位B */ "imageAllowFiles": [".png", ".jpg", ".jpeg", ".gif", ".bmp"], /* 上传图片格式显示 */ "imageCompressEnable": true, /* 是否压缩图片,默认是true */ "imageCompressBorder": 1600, /* 图片压缩最长边限制 */ "imageInsertAlign": "none", /* 插入的图片浮动方式 */ "imageUrlPrefix": "http://localhost:8081/", /* 图片访问路径前缀 */ "imagePathFormat": "/upload/image/{yyyy}{mm}{dd}/{time}{rand:6}", /* 上传保存路径,可以自定义保存路径和文件名格式 */ /* {filename} 会替换成原文件名,配置这项需要注意中文乱码问题 */ /* {rand:6} 会替换成随机数,后面的数字是随机数的位数 */ /* {time} 会替换成时间戳 */ /* {yyyy} 会替换成四位年份 */ /* {yy} 会替换成两位年份 */ /* {mm} 会替换成两位月份 */ /* {dd} 会替换成两位日期 */ /* {hh} 会替换成两位小时 */ /* {ii} 会替换成两位分钟 */ /* {ss} 会替换成两位秒 */ /* 非法字符 \ : * ? " < > | */ /* 具请体看线上文档: fex.baidu.com/ueditor/#use-format_upload_filename */ /* 涂鸦图片上传配置项 */ "scrawlActionName": "uploadscrawl", /* 执行上传涂鸦的action名称 */ "scrawlFieldName": "upfile", /* 提交的图片表单名称 */ "scrawlPathFormat": "/upload/image/{yyyy}{mm}{dd}/{time}{rand:6}", /* 上传保存路径,可以自定义保存路径和文件名格式 */ "scrawlMaxSize": 2048000, /* 上传大小限制,单位B */ "scrawlUrlPrefix": "", /* 图片访问路径前缀 */ "scrawlInsertAlign": "none", /* 截图工具上传 */ "snapscreenActionName": "uploadimage", /* 执行上传截图的action名称 */ "snapscreenPathFormat": "/upload/image/{yyyy}{mm}{dd}/{time}{rand:6}", /* 上传保存路径,可以自定义保存路径和文件名格式 */ "snapscreenUrlPrefix": "", /* 图片访问路径前缀 */ "snapscreenInsertAlign": "none", /* 插入的图片浮动方式 */ /* 抓取远程图片配置 */ "catcherLocalDomain": ["127.0.0.1", "localhost", "img.baidu.com"], "catcherActionName": "catchimage", /* 执行抓取远程图片的action名称 */ "catcherFieldName": "source", /* 提交的图片列表表单名称 */ "catcherPathFormat": "/upload/image/{yyyy}{mm}{dd}/{time}{rand:6}", /* 上传保存路径,可以自定义保存路径和文件名格式 */ "catcherUrlPrefix": "", /* 图片访问路径前缀 */ "catcherMaxSize": 2048000, /* 上传大小限制,单位B */ "catcherAllowFiles": [".png", ".jpg", ".jpeg", ".gif", ".bmp"], /* 抓取图片格式显示 */ /* 上传视频配置 */ "videoActionName": "uploadvideo", /* 执行上传视频的action名称 */ "videoFieldName": "upfile", /* 提交的视频表单名称 */ "videoPathFormat": "/upload/video/{yyyy}{mm}{dd}/{time}{rand:6}", /* 上传保存路径,可以自定义保存路径和文件名格式 */ "videoUrlPrefix": "http://localhost:8081/", /* 视频访问路径前缀 */ "videoMaxSize": 102400000, /* 上传大小限制,单位B,默认100MB */ "videoAllowFiles": [ ".flv", ".swf", ".mkv", ".avi", ".rm", ".rmvb", ".mpeg", ".mpg", ".ogg", ".ogv", ".mov", ".wmv", ".mp4", ".webm", ".mp3", ".wav", ".mid"], /* 上传视频格式显示 */ /* 上传文件配置 */ "fileActionName": "uploadfile", /* controller里,执行上传视频的action名称 */ "fileFieldName": "upfile", /* 提交的文件表单名称 */ "filePathFormat": "/upload/file/{yyyy}{mm}{dd}/{time}{rand:6}", /* 上传保存路径,可以自定义保存路径和文件名格式 */ "fileUrlPrefix": "http://localhost:8081/", /* 文件访问路径前缀 */ "fileMaxSize": 51200000, /* 上传大小限制,单位B,默认50MB */ "fileAllowFiles": [ ".png", ".jpg", ".jpeg", ".gif", ".bmp", ".flv", ".swf", ".mkv", ".avi", ".rm", ".rmvb", ".mpeg", ".mpg", ".ogg", ".ogv", ".mov", ".wmv", ".mp4", ".webm", ".mp3", ".wav", ".mid", ".rar", ".zip", ".tar", ".gz", ".7z", ".bz2", ".cab", ".iso", ".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx", ".pdf", ".txt", ".md", ".xml" ], /* 上传文件格式显示 */ /* 列出指定目录下的图片 */ "imageManagerActionName": "listimage", /* 执行图片管理的action名称 */ "imageManagerListPath": "/upload/image/", /* 指定要列出图片的目录 */ "imageManagerListSize": 20, /* 每次列出文件数量 */ "imageManagerUrlPrefix": "http://localhost:8081/", /* 图片访问路径前缀 */ "imageManagerInsertAlign": "none", /* 插入的图片浮动方式 */ "imageManagerAllowFiles": [".png", ".jpg", ".jpeg", ".gif", ".bmp"], /* 列出的文件类型 */ /* 列出指定目录下的文件 */ "fileManagerActionName": "listfile", /* 执行文件管理的action名称 */ "fileManagerListPath": "/upload/file/", /* 指定要列出文件的目录 */ "fileManagerUrlPrefix": "http://localhost:8081/", /* 文件访问路径前缀 */ "fileManagerListSize": 20, /* 每次列出文件数量 */ "fileManagerAllowFiles": [ ".png", ".jpg", ".jpeg", ".gif", ".bmp", ".flv", ".swf", ".mkv", ".avi", ".rm", ".rmvb", ".mpeg", ".mpg", ".ogg", ".ogv", ".mov", ".wmv", ".mp4", ".webm", ".mp3", ".wav", ".mid", ".rar", ".zip", ".tar", ".gz", ".7z", ".bz2", ".cab", ".iso", ".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx", ".pdf", ".txt", ".md", ".xml" ] /* 列出的文件类型 */ }
步骤4.创建一个Uploader类,其他代码反编译百度,加入红色内容,通过配置文件配置是上传本地服务器还是远程FTP服务器
package com.xxxx.ueditor.upload; import com.baidu.ueditor.define.State; import com.baidu.ueditor.upload.Base64Uploader; import com.baidu.ueditor.upload.BinaryUploader; import java.util.Map; import javax.servlet.http.HttpServletRequest; public class Uploader { private HttpServletRequest request = null; private Map<String, Object> conf = null; public Uploader(HttpServletRequest request, Map<String, Object> conf) { this.request = request; this.conf = conf; } public final State doExec() { String filedName = (String)this.conf.get("fieldName"); State state = null; //保留原有逻辑,在json.config中加入是否使用FTP上传配置项 if ("true".equals(this.conf.get("isBase64"))) state = Base64Uploader.save(this.request.getParameter(filedName), this.conf); else { if("true".equals(this.conf.get("useFtpUpload"))) state = FtpUploader.save(request, conf); else state = BinaryUploader.save(this.request, this.conf); } return state; } }
步骤5.创建一个FtpUploader类,内容是反编译的BinaryUploader,稍作修改,保证在远程服务器上创建路径和本地服务器的一致
package com.xxxx.ueditor.upload; import com.baidu.ueditor.PathFormat; import com.baidu.ueditor.define.BaseState; import com.baidu.ueditor.define.FileType; import com.baidu.ueditor.define.State; 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 FtpUploader { 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, 5); } 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, 7); } 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, 8); } savePath = PathFormat.parse(savePath, originFileName); String remoteDir = ""; int pos = savePath.lastIndexOf("/"); if(pos > -1){ remoteDir = savePath.substring(0,pos + 1); } String physicalPath = (String)conf.get("rootPath") + savePath; boolean keepLocalFile = "false".equals(conf.get("keepLocalFile")) ? false : true; InputStream is = fileStream.openStream(); State storageState = StorageManager.saveFtpFileByInputStream(is, remoteDir, physicalPath, maxSize, keepLocalFile); is.close(); if (storageState.isSuccess()) { storageState.putInfo("url", savePath); storageState.putInfo("type", suffix); storageState.putInfo("original", originFileName + suffix); } return storageState; } catch (FileUploadException e) { return new BaseState(false, 6); } catch (IOException localIOException) { } return new BaseState(false, 4); } @SuppressWarnings("rawtypes") private static boolean validType(String type, String[] allowTypes) { List list = Arrays.asList(allowTypes); return list.contains(type); } }
步骤6.创建一个StorageManager类,内容反编译百度原有内容,加入几个FTP相关函数
package com.xxxx.ueditor.upload; import com.baidu.ueditor.define.BaseState; import com.baidu.ueditor.define.State; import com.xxxx.common.util.FileToFTP; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import org.apache.commons.io.FileUtils; public class StorageManager { public static final int BUFFER_SIZE = 8192; public static State saveBinaryFile(byte[] data, String path) { File file = new File(path); State state = valid(file); if (!state.isSuccess()) { return state; } try { BufferedOutputStream bos = new BufferedOutputStream( new FileOutputStream(file)); bos.write(data); bos.flush(); bos.close(); } catch (IOException ioe) { return new BaseState(false, 4); } state = new BaseState(true, file.getAbsolutePath()); state.putInfo("size", data.length); state.putInfo("title", file.getName()); return state; } public static State saveFileByInputStream(InputStream is, String path, long maxSize) { State state = null; File tmpFile = getTmpFile(); byte[] dataBuf = new byte[2048]; BufferedInputStream bis = new BufferedInputStream(is, 8192); try { BufferedOutputStream bos = new BufferedOutputStream( new FileOutputStream(tmpFile), 8192); int count = 0; while ((count = bis.read(dataBuf)) != -1) { bos.write(dataBuf, 0, count); } bos.flush(); bos.close(); if (tmpFile.length() > maxSize) { tmpFile.delete(); return new BaseState(false, 1); } state = saveTmpFile(tmpFile, path); if (!state.isSuccess()) { tmpFile.delete(); } return state; } catch (IOException localIOException) { } return new BaseState(false, 4); } public static State saveFileByInputStream(InputStream is, String path) { State state = null; File tmpFile = getTmpFile(); byte[] dataBuf = new byte[2048]; BufferedInputStream bis = new BufferedInputStream(is, 8192); try { BufferedOutputStream bos = new BufferedOutputStream( new FileOutputStream(tmpFile), 8192); int count = 0; while ((count = bis.read(dataBuf)) != -1) { bos.write(dataBuf, 0, count); } bos.flush(); bos.close(); state = saveTmpFile(tmpFile, path); if (!state.isSuccess()) { tmpFile.delete(); } return state; } catch (IOException localIOException) { } return new BaseState(false, 4); } private static File getTmpFile() { File tmpDir = FileUtils.getTempDirectory(); double d = Math.random() * 10000.0D; String tmpFileName = String.valueOf(d).replace(".", ""); return new File(tmpDir, tmpFileName); } private static State saveTmpFile(File tmpFile, String path) { State state = null; File targetFile = new File(path); if (targetFile.canWrite()) return new BaseState(false, 2); try { FileUtils.moveFile(tmpFile, targetFile); } catch (IOException e) { return new BaseState(false, 4); } state = new BaseState(true); state.putInfo("size", targetFile.length()); state.putInfo("title", targetFile.getName()); return state; } private static State valid(File file) { File parentPath = file.getParentFile(); if ((!parentPath.exists()) && (!parentPath.mkdirs())) { return new BaseState(false, 3); } if (!parentPath.canWrite()) { return new BaseState(false, 2); } return new BaseState(true); } /** * 上传FTP文件 * @param is * @param path * @param maxSize * @return */ public static State saveFtpFileByInputStream(InputStream is, String remoteDir, String path, long maxSize,boolean keepLocalFile) { State state = null; File tmpFile = getTmpFile(); byte[] dataBuf = new byte[2048]; BufferedInputStream bis = new BufferedInputStream(is, 8192); try { BufferedOutputStream bos = new BufferedOutputStream( new FileOutputStream(tmpFile), 8192); int count = 0; while ((count = bis.read(dataBuf)) != -1) { bos.write(dataBuf, 0, count); } bos.flush(); bos.close(); if (tmpFile.length() > maxSize) { tmpFile.delete(); return new BaseState(false, 1); } state = saveFtpTmpFile(tmpFile, remoteDir, path, keepLocalFile); if (!state.isSuccess()) { tmpFile.delete(); } return state; } catch (IOException localIOException) { } return new BaseState(false, 4); } private static State saveFtpTmpFile(File tmpFile, String remoteDir, String path,boolean keepLocalFile) { State state = null; File targetFile = new File(path); if (targetFile.canWrite()) return new BaseState(false, 2); try { FileUtils.moveFile(tmpFile, targetFile); } catch (IOException e) { return new BaseState(false, 4); } try { FileToFTP t = new FileToFTP(); t.connect(""); if(! t.uploadWithRemoteDir(remoteDir,targetFile)){ return new BaseState(false, 4); } }catch (Exception e) { return new BaseState(false, 4); } try { if(! keepLocalFile) targetFile.delete(); }catch(Exception e){ } state = new BaseState(true); state.putInfo("size", targetFile.length()); state.putInfo("title", targetFile.getName()); return state; } }
到此就完成了UEditor上传FTP服务器的工作了.