這兩天要實現一個文件上傳的功能。
開發環境是:服務器端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>