`

FCKEditor上传文件与Struts2的完美结合

 
阅读更多
FCKEditor是2.6版本,Struts为2.1版本。

FCKEditor与Java的连接上传封装在ConnectorServlet里,用的是commons-fileupload组件解析request。
在此不细表此基础知识,不懂请Google一把FCKEditor的Java上传配置,再看看ConnectorServlet的源码。

问题是:博客使用了Struts2框架,Struts2的Filter会过滤到上传文件的request,封装为Struts2自己的MultiPartRequestWrapper,这个时候再到FCKEditor里的ConnectorServlet中用commons-fileupload组件解析request,就无法获取到正确的文件流,所以上传失败。
之所以原来是没问题的,是因为当时web.xml配置的Struts2只过滤后缀为action的请求,对于上传文件的请求不做处理。而后来更新了URLRewrite,更改Struts2的Filter配置为过滤所有的请求,上传文件自然也就被过滤到了。

翻查了一下网络上的解决方案,都是更改Struts2的web.xml配置,只过滤后缀为action的请求或者其他struts2需要处理的请求。
这个解决方案在本博客基本行不通,因为使用URLRewrite并且伪装url的原因,绝大部分url都需要匹配Struts2的过滤器,不可能一个一个去配,web.xml中的Filter貌似又没有类似exclude的配置方式,这可头疼了。

除此之外,解决这个冲突不外乎两个方案,一是改造FCKEditor的ConnectorServlet,二是改造Struts2的Filter。

改造Struts2的Filter意味着所有上传文件的request都不再封装了,这恐怕不合适,万一以后我其他地方要用到呢?

那就只有改造FCKEditor的ConnectorServlet了,这还是有两条路,第一条路是彻底改变FCKEditor的Java上传方式,抛弃原来的ConnectorServlet,改为Struts2中的Action,第二条路是对ConnectorServlet进行有限改造,让它在解析request时能够解析Struts2封装的MultiPartRequestWrapper。

其实第一条路更好些,这样FCKEditor的文件上传就在Struts2的Action管理体系下,方便进行Struts2拦截器等一系列配置,同时也就更方便地解决了安全问题,不用对ConnectorServlet做安全性处理比如专门加个Filter或者在ConnectorServlet中做一些侵入性的判断。至于这么做是否麻烦,我还没有尝试,因为目前我走的是第二条路。但以后一定会这么做,大家可以等待本文的续篇了,。

第二条路,Struts2本身的文件上传默认使用的就是commons-fileupload组件,理论上是行得通,只是要仔细研究一下ConnectorServlet使用commons-fileupload组件解析request的方式。

当真正开始去看的时候,我才发现这TMD其实不那么简单,虽然最终写出来确实没几行代码,但要把Struts2中封装request、对上传请求拦截和commons-fileupload解析request的那一堆源码看懂了分析明白了并且让两者能对应得上才是要命的功夫……
更要命的是Struts2封装的MultiPartRequestWrapper对象只提供了一些直接面对文件信息的public方法,并没有把被封装的原始request开放出来,否则就简单了,我直接获取原始request传递给commons-fileupload就哦了。难道要让我用反射不成?

又看源码,发现ConnectorServlet使用commons-fileupload解析request生成了一个FileItem对象用于存储文件信息,OK,这可能就是突破口。我能不能先获取MultiPartRequestWrapper对象里封装的文件信息,然后绕过原有的解析,仿照commons-fileupload的源码重新封装生成一个FileItem对象去使用呢?

按照这个思路,参照源码,一边写一边测一边想一边改,好几个参数绕来绕去,有时就是靠猜,终于调试成功,于是形成了以下的代码。因为FCKEditor上传文件是ConnectorServlet中的doPost方法,所以我将改造之后的doPost方法完整贴出来,按照注释应该可以看懂:
public void doPost(HttpServletRequest request, HttpServletResponse response)
	        throws ServletException, IOException {
		logger.debug("Entering Connector#doPost");

		response.setCharacterEncoding("UTF-8");
		response.setContentType("text/html; charset=UTF-8");
		response.setHeader("Cache-Control", "no-cache");
		PrintWriter out = response.getWriter();

		String commandStr = request.getParameter("Command");
		String typeStr = request.getParameter("Type");
		String currentFolderStr = request.getParameter("CurrentFolder");

		logger.debug("Parameter Command: {}", commandStr);
		logger.debug("Parameter Type: {}", typeStr);
		logger.debug("Parameter CurrentFolder: {}", currentFolderStr);

		UploadResponse ur;

		// if this is a QuickUpload request, 'commandStr' and 'currentFolderStr'
		// are empty
		if (Utils.isEmpty(commandStr) && Utils.isEmpty(currentFolderStr)) {
			commandStr = "QuickUpload";
			currentFolderStr = "/";
		}

		if (!RequestCycleHandler.isEnabledForFileUpload(request))
			ur = new UploadResponse(UploadResponse.SC_SECURITY_ERROR, null, null,
			        Messages.NOT_AUTHORIZED_FOR_UPLOAD);
		else if (!CommandHandler.isValidForPost(commandStr))
			ur = new UploadResponse(UploadResponse.SC_ERROR, null, null, Messages.INVALID_COMMAND);
		else if (typeStr != null && !ResourceTypeHandler.isValid(typeStr))
			ur = new UploadResponse(UploadResponse.SC_ERROR, null, null, Messages.INVALID_TYPE);
		else if (!UtilsFile.isValidPath(currentFolderStr))
			ur = UploadResponse.UR_INVALID_CURRENT_FOLDER;
		else {
			ResourceTypeHandler resourceType = ResourceTypeHandler.getDefaultResourceType(typeStr);

			String typePath = UtilsFile.constructServerSidePath(request, resourceType);
			String typeDirPath = getServletContext().getRealPath(typePath);

			File typeDir = new File(typeDirPath);
			UtilsFile.checkDirAndCreate(typeDir);

			File currentDir = new File(typeDir, currentFolderStr);

			if (!currentDir.exists())
				ur = UploadResponse.UR_INVALID_CURRENT_FOLDER;
			else {

				String newFilename = null;
				FileItemFactory factory = new DiskFileItemFactory();
				ServletFileUpload upload = new ServletFileUpload(factory);

				upload.setHeaderEncoding("UTF-8");//解决上传中文文件名问题
				
				try {
					//---------------FCKEditor上传文件与Struts2结合改造-----开始----------------------------
					FileItem uplFile = null;
					//基本思路:如果request为Struts2封装的MultiPartRequestWrapper,则自行解析该request,重新构造FileItem,将Struts2上传的临时文件传递给FileItem
					if(request instanceof MultiPartRequestWrapper){
						//转为Struts2封装的MultiPartRequestWrapper
						MultiPartRequestWrapper mr = (MultiPartRequestWrapper)request;

						//通过MultiPartRequestWrapper的方法取得相应的属性
						Enumeration<String> e = mr.getFileParameterNames();
						String fieldName = "";
						//只获取第一个文件
						if(e.hasMoreElements()){
							fieldName = e.nextElement();
						}
						//获取Struts2封装好的临时文件信息
						File file = mr.getFiles(fieldName)[0];
						String fileName = mr.getFileNames(fieldName)[0];
						String contentType = mr.getContentTypes(fieldName)[0];
						//使用fileupload组件API重新构造一个FileItem
						DiskFileItemFactory fac = new DiskFileItemFactory();
					    fac.setSizeThreshold(0);
					    fac.setRepository(file.getParentFile());
					    uplFile = fac.createItem(fieldName, contentType, false, fileName);
					    //将Struts2封装好的临时文件传递给这个FileItem
					    Streams.copy(new FileInputStream(file), uplFile.getOutputStream(),
	                            true);
					}else{
						//如果不是Struts2封装的MultiPartRequestWrapper,按原有执行
						//-----------以下两行基本是原有代码---------------
						List<FileItem> items = upload.parseRequest(request);

						uplFile = items.get(0);
					}
					//---------------FCKEditor上传文件与Struts2结合改造-----结束----------------------------
					String rawName = UtilsFile.sanitizeFileName(uplFile.getName());
					String filename = FilenameUtils.getName(rawName);
					String baseName = FilenameUtils.removeExtension(filename);
					String extension = FilenameUtils.getExtension(filename);

					if (!ExtensionsHandler.isAllowed(resourceType, extension))
						ur = new UploadResponse(UploadResponse.SC_INVALID_EXTENSION);
					else {
						
						// construct an unique file name
						File pathToSave = new File(currentDir, filename);
						int counter = 1;
						while (pathToSave.exists()) {
							newFilename = baseName.concat("(").concat(String.valueOf(counter))
							        .concat(")").concat(".").concat(extension);
							pathToSave = new File(currentDir, newFilename);
							counter++;
						}

						if (Utils.isEmpty(newFilename))
							ur = new UploadResponse(UploadResponse.SC_OK, UtilsResponse
							        .constructResponseUrl(request, resourceType, currentFolderStr,
							                true, ConnectorHandler.isFullUrl()).concat(filename));
						else
							ur = new UploadResponse(UploadResponse.SC_RENAMED,
							        UtilsResponse.constructResponseUrl(request, resourceType,
							                currentFolderStr, true, ConnectorHandler.isFullUrl())
							                .concat(newFilename), newFilename);

						// secure image check
						if (resourceType.equals(ResourceTypeHandler.IMAGE)
						        && ConnectorHandler.isSecureImageUploads()) {
							if (UtilsFile.isImage(uplFile.getInputStream()))
								uplFile.write(pathToSave);
							else {
								uplFile.delete();
								ur = new UploadResponse(UploadResponse.SC_INVALID_EXTENSION);
							}
						} else
							uplFile.write(pathToSave);

					}
				} catch (Exception e) {
					ur = new UploadResponse(UploadResponse.SC_SECURITY_ERROR);
				}
			}

		}

		out.print(ur);
		out.flush();
		out.close();

		logger.debug("Exiting Connector#doPost");
	}



好了,任务完成。代码里还包括了解决中文问题,这个网上有其他文章说明,看官可自行搜索……
分享到:
评论
1 楼 hongyuan19 2011-12-21  
哥们,转载请注明出处,谢谢。
http://blog.eyougo.com/blog/view/27

相关推荐

    Fckeditor完美结合Struts2 教程

    在本文中,我们将深入探讨如何将富文本编辑器Fckeditor完美地整合到Struts2框架中,以便在Struts2项目中充分利用Fckeditor的图片上传和其他功能。Fckeditor是一款强大的在线文本编辑器,它提供了丰富的文本格式化...

    struts2,fckeditor冲突解决

    在使用Struts2与FCKeditor集成时,可能会遇到上传图片的冲突问题。这个问题主要出现在以下方面: 1. **Action配置**:Struts2通过配置Action来处理HTTP请求。在使用FCKeditor上传图片时,如果没有正确配置Action,...

    struts2+fckeditor

    在尝试将FCKeditor与Struts2整合时,最常见的问题之一是图片上传失败。这个问题通常由以下几个原因造成: 1. **Action配置**:首先,确保你的Struts2 Action配置正确。Action应该接收FCKeditor发送的文件,并将其...

    Struts2与FCKeditor的整合.doc

    Struts2 和 FCKeditor 的整合是Web开发中一个常见的需求,特别是在需要提供富文本编辑器功能时。FCKeditor是一款强大的开源JavaScript文本编辑器,而Struts2是一个流行的Java Web应用框架。本文将详细讲解如何将...

    fckeditor上传文件绝对路径设置方法

    在使用FCKEditor这款强大的富文本编辑器时,有时我们需要自定义上传文件的路径,使其成为绝对路径,以便更好地管理和访问资源。FCKEditor默认情况下,上传文件的路径通常是相对路径,这可能在多服务器环境或者需要...

    FCKeditor文件上传漏洞及利用-File-Upload-Vulnerability-in-FCKEditor1

    【FCKeditor文件上传漏洞及利用 - File-Upload-Vulnerability-in-FCKEditor1】 本文主要探讨了FCKeditor(现称为CKeditor)中的PHP文件上传模块存在的安全漏洞,允许攻击者绕过文件类型检查,将恶意PHP代码上传到...

    FCKeditor添加FLV视频和上传文件自动更名

    FCKeditor(jsp版本)目录文件,FCKeditor.jar包,增加FCKeditor添加FLV视频功能和FCKeditor上传文件自动更名功能 1.修正了上传中文文件乱码的问题,上传之后会重命名文件 2.修正了不能上传FLV视频的问题

    手动 扩展Fckeditor 上传文件 功能

    手动 扩展Fckeditor 上传文件 功能

    FCKeditor视频上传插件

    2. **上传处理**:选定的视频文件会被上传到服务器。在这个过程中,可能涉及到文件大小限制、格式检查以及转码操作(如果服务器不支持原始视频格式)。 3. **预览与插入**:上传成功后,编辑器会生成一段HTML代码,...

    FCKeditor ssh2项目 jar包配置页面文件

    - `struts2-fckeditor-plugin-x.x.x.jar`:Struts2的FCKeditor插件,提供与Struts2框架的整合。 - `commons-fileupload.jar`和`commons-io.jar`:用于文件上传的支持库。 - 可能还需要SSH2框架相关的其他依赖包,...

    为fckeditor添加多文件批量上传组件

    2. **集成组件**:将所选的上传组件的JavaScript和CSS文件引入到FCKeditor的页面中。确保它们与FCKeditor的样式和脚本兼容,并且不会冲突。 3. **创建上传按钮**:在FCKeditor的工具栏中添加一个用于触发批量上传的...

    struts和fckeditor整合完整版

    5. **处理上传文件**:FCKeditor支持文件上传功能,但如描述中所述,这个整合版本可能不支持中文文件名的上传。这可能是因为服务器端没有正确处理编码问题,或者文件上传的路径设置不支持中文字符。解决这个问题通常...

    FCKEDITOR 常用上传方法

    FCKEditor使用连接器来与服务器进行通信,处理文件的创建、删除、移动等操作。例如: - `FCKeditor/editor/filemanager/connectors/asp/connector.asp` - `FCKeditor/editor/filemanager/connectors/...

    fckeditor编辑器上传文件(含视频音频)详细配置

    FCKeditor默认可以上传文件、图片、Flash和多媒体四种类型的文件,可以在SetConfig()中对AllowedTypes修改来增减允许的类型(这个类型可以在后面的TypeConfig["TypeName"]中定义,比如在AllowedExtensions属性中定制...

    struts2+FCKeditor

    总的来说,Struts2结合FCKeditor能有效提升Web应用的交互性和用户体验。开发者只需要按照一定的步骤进行配置和编程,就可以轻松实现富文本编辑功能。同时,理解并掌握Struts2的Action、结果视图以及FCKeditor的配置...

    兼容struts2的FCKEditor(插件).rar

    5. **文件上传**:FCKEditor支持文件上传功能,这需要在Struts2中配置文件上传拦截器,设置允许的最大文件大小和类型。同时,需要提供一个Action来接收并保存上传的文件。 6. **安全考虑**:整合过程中需要注意XSS...

    FCKeditor编辑器2.63(解决无法上传文件)

    在FCKeditor 2.63中,上传文件功能的优化解决了困扰用户的"上传过程中的进度条问题"。这通常指的是文件上传时,进度条卡在某个位置无法完成的情况。这个问题可能由多种原因引起,包括服务器端的配置错误、网络延迟或...

Global site tag (gtag.js) - Google Analytics