这两天要实现一个文件上传的功能。
开发环境是:服务器端Struts2,客户端jQuery,需要使用Ajax来实现,我在用Struts2开发Ajax应用时,都是基于JSON来实现的。
理论上文件上传是无法用Ajax来实现的:ajax基于javascript和xmlhtttp,其与服务器通信都是通过传递字符串。另外出于安全考虑,javascript是不能操作文件的,因此从理论上来说javascript是无法实现文件上传的。
实际上只能实现一种无刷新文件上传,从实现效果上类似于Ajax的无刷新与服务器进行通信。
其原理是:给file控件所在的form加上 target 属性,将返回结果页面定向到一个隐藏的 iframe。
在具体的实现上,我使用了jQuery的form插件来完成,该插件对文件上传的功能进行了封装,其实现原理也类似于上面的分析。但可以方面的在进行提交之前与提交之后添加callback函数。
在实现中出现了一个问题是:如果用Struts2的JSON返回结果,上传完成以后,不会调用form插件success对应的callback函数,并自动弹出JSON信息的下载页面,使用记事本打开可以看到对应的JSON信息。
对于这个问题有两个解决办法:
1. 不使用JSON方法,将Struts2的结果类型换成redirect
2. 不使用JSON方法,将Struts2的结果类型换成plaintext,直接输出处理情况信息
其他的一些解决办法是:可以使用Flex相关的技术来实现,看了一下Google Doc的文件上传,就是使用flash来实现的。
plaintext可以直接将处理结果信息写到Response,不需要另外创建新的结果页面,最后我采用了这种办法。 具体如下:
代码情况如下:
JSP页面:uploadDoc.jsp
<%@ page contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> <%@ taglib prefix="s" uri="/struts-tags"%> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>上传文件</title> <script src="../js/jquery.js" type="text/javascript"></script> <script src="../js/jquery.form.js" type="text/javascript"></script> <script language="javascript"> $(document).ready(function(){ var validateForm = function() { var fileName = $('#theFile').val(); var m=parseInt(fileName.toString().lastIndexOf("."))+1; var extVal=fileName.toString().substr(m); if(extVal!="doc") { alert('文件类型必须为Word文件!'); return false; } $('#upMessage').html('文件上传中,请等待... ...'); return true; }; var showResponse = function(data,status) { $('#upMessage').fadeIn("fast",function(){ }); return true; }; var options={ target : '#upMessage', url : 'AjaxUploadProductDoc!upload.action', beforeSubmit: validateForm, success : showResponse, resetForm:true }; $('#upForm').ajaxForm(options); }); </script> </head> <body> <form id="upForm" method="POST" enctype="multipart/form-data"> 上传文件:<input type="file" name="file" id="theFile"/> <br/> <input type="submit" value="提交" /> <div id="upMessage" style="displan:hidden"></div> </form> </body> </html>
后台Java类: AjaxUploadProductDocAction.java
package org.xtwh.product.action;
import org.apache.log4j.Logger;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.Date;
import java.util.Map;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
/**
* 处理上传DOC文件
* @author user
*
*/
public class AjaxUploadProductDocAction extends ActionSupport{
/**
* Logger for this class
*/
private static final Logger logger = Logger
.getLogger(AjaxUploadProductDocAction.class);
private static final int BUFFER_SIZE = 16 * 1024;
private File file;
private String contentType; //上传文件的MIME类型
private String fileName; //上传文件的文件名,该文件名不包括文件的后缀
private boolean success; //标识上传是否成功
private static final String TEXT = "text";
//访问页面
public String execute() {
return INPUT;
}
//上传文件
public String upload() throws IOException {
if(fileName == null || fileName.equals("")) {
success = false;
printMessage();
return TEXT;
}
if (file != null) {
//保存文件
copy(file, docFile);
//进行其他处理
}
success = true;
message = "上传文件成功。";
printMessage();
return TEXT;
}
/**
* 判断上传文件夹中是否有同名文件
* @return
*/
private boolean hasSameFile() {
return false;
}
//输出信息到response
private void printMessage() throws IOException {
ActionContext ctx = ActionContext.getContext();
HttpServletResponse response = (HttpServletResponse)ctx.get(ServletActionContext.HTTP_RESPONSE);
response.setContentType("text/plain;charset=utf-8");
response.setHeader("Cache-Control", "no-cache");
PrintWriter pw = null;
try {
StringBuffer sb = new StringBuffer();
if(success) {
sb.append("上传文件成功!");
}else {
sb.append("上传文件失败");
}
pw = response.getWriter();
pw.print(sb.toString());
} catch (IOException e) {
logger.error("输出 错误!");
e.printStackTrace();
}
finally {
if(pw != null) {
pw.close();
pw = null;
}
}
}
//copy file
private static void copy(File src, File dst) {
try {
InputStream in = null ;
OutputStream out = null ;
try {
in = new BufferedInputStream( new FileInputStream(src), BUFFER_SIZE);
out = new BufferedOutputStream( new FileOutputStream(dst), BUFFER_SIZE);
byte [] buffer = new byte [BUFFER_SIZE];
while (in.read(buffer) > 0 ) {
out.write(buffer);
}
} finally {
if ( null != in) {
in.close();
}
if ( null != out) {
out.close();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public boolean isSuccess() {
return success;
}
public void setFile(File file) {
this.file = file;
}
public void setFileContentType(String contentType) {
this.contentType = contentType;
}
public void setFileFileName(String fileName) {
this.fileName = fileName;
}
}
配置文件:
<action name="AjaxUploadProductDoc" class="org.xtwh.product.action.AjaxUploadProductDocAction"> <result name="input">/product/uploadDoc.jsp</result> <result name="xml" type="plaintext"></result> </action>