Servlet3.0.1與dojo實現圖片AJAX上傳與下載
在servlet2.5中,并沒有處理文件上傳二進制流的API,一般都是借用Apache Commons下的fileUploader。在servlet3.0以后,文件上傳的request處理已經被納入servlet jsr. 各大主流web容器也都支持了新的jcr。而servlet3.0.1則更進一步支持文件上傳,tomcat8完全實現了servlet3.0.1. 這篇文章將使用servlet3.0.1做后端,dojo做前端,制作一個圖片AJAX上傳于下載網站。
開發環境
tomcat8, eclipse+maven,dojo1.9
Maven項目
首先需要生成一個webapp的maven項目。本次開發用到了tomcat的servlet api, jsp api以及jstl,因此需要在pom.xml中聲明所需的依賴。為了不超過字數上限,我只列出依賴的ID。
tomcat-servlet-api tomcat-jsp-api jstl
我們將有兩個servlet和一個JSP
index.jsp, 負責界面展示。對應的URL為http://localhost:8080/images
UploaderServlet, 負責處理dojo發送的AJAX圖片上傳請求,并返回JSON格式的消息。對應的URL為http://localhost:8080/images/ajaxUploader
ImagesServlet, 負責處理對上傳后的圖片訪問請求。對應的URL為http://localhost:8080/images/*.png|jpg|gif?download=true|false
后端開發
web.xml
Servlet3可以使用annotation config,也可以使用傳統的web.xml。我們還是用web.xml.
<?xml version="1.0" encoding="UTF-8"?> <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"> <display-name>Archetype Created Web Application</display-name> <context-param> <!-- 定義上傳圖片最終存放的文件夾。 --> <param-name>shareFolder</param-name> <param-value>d:/share/data</param-value> </context-param> <servlet> <servlet-name>imageUploader</servlet-name> <servlet-class>servlets.UploaderServlet</servlet-class> <multipart-config> <!--臨時存放上傳文件的地方--> <location>d:/share/tmp</location> <!--允許上傳的文件最大值,約10MB。默認值為 -1,表示沒有限制。--> <max-file-size>10240000</max-file-size> <!--針對該 multipart/form-data 請求的最大數量,默認值為 -1,表示沒有限制。--> <max-request-size>20480000</max-request-size> <!--當數據量大于約10MB時,內容將被寫入tmp目錄下的文件。--> <file-size-threshold>10240000</file-size-threshold> </multipart-config> </servlet> <servlet-mapping> <servlet-name>imageUploader</servlet-name> <url-pattern>/ajaxUploader</url-pattern> </servlet-mapping> <servlet> <servlet-name>imageAccess</servlet-name> <servlet-class>servlets.ImagesServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>imageAccess</servlet-name> <url-pattern>*.png</url-pattern> <url-pattern>*.jpg</url-pattern> <url-pattern>*.gif</url-pattern> </servlet-mapping> </web-app>
從上可見,所有的圖片訪問均由ImagesServlet處理,文件上傳由UploaderServlet處理。UploaderServlet下面配置了文件上傳的限制,請看注釋。另外我還聲明了一個context-param,作用請看注釋。
UploaderServlet
使用request.getPart()方法,可以得到文件上傳part的對象。將上傳的流保存在指定的目錄文件中。
特別注意的是,此servlet響應的將是dojo.io.iframe發送來的AJAX請求,因此,返回JSON格式的響應,將是最好的選擇。
響應dojo.io.iframe的JSON格式,必須包含在下面的結構中。這是dojo.io.iframe所能解析的結構,也是必須遵循的規則:
<html> <body> JSON_DATA </body> </html>
UploadServlet的部分代碼如下:
public class UploaderServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
try {
Part image = req.getPart("image");
//servlet3.0.1新加入的方法,在3中,我們只能通過header來自己解析filename.
String imageName = image.getSubmittedFileName();
String shareFolder = req.getServletContext().getInitParameter("shareFolder");
File imageFile = new File(shareFolder, imageName);
FileOutputStream fos=null;
InputStream is =null;
try {
fos = new FileOutputStream(imageFile);
is = image.getInputStream();
byte[] bytes = new byte[4096];
int bytesReaded = -1;
while ((bytesReaded=is.read(bytes))!=-1) {
fos.write(bytes, 0, bytesReaded);
}
fos.flush();
} finally {
//關閉所有的流。
}
sendCreated(resp, imageName);
} catch (Exception e) {
e.printStackTrace();
sendError(resp);
return;
}
}
private void sendError(HttpServletResponse resp) {
resp.setStatus(200);
resp.setContentType("text/html;charset=UTF-8");
StringBuilder body = new StringBuilder()
.append("<html> ")
.append(" <boby> ")
.append(" <textarea> ")
.append(" {status:500, message:'Can't load your image.'} ")
.append(" </textarea> ")
.append(" </body> ")
.append("</html> ");
resp.setContentLength(body.length());
PrintWriter out;
try {
out = resp.getWriter();
out.print(body.toString());
out.flush();
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
private void sendCreated(HttpServletResponse resp, String image) {
resp.setStatus(200);
resp.setContentType("text/html;charset=UTF-8");
StringBuilder body = new StringBuilder()
.append("<html> ")
.append(" <boby> ")
.append(" <textarea> ")
.append(String.format("{status:201, image:'%s', message:'File created.'}", image))
.append(" </textarea> ")
.append(" </body> ")
.append("</html> ");
resp.setContentLength(body.length());
PrintWriter out;
try {
out = resp.getWriter();
out.print(body.toString());
out.flush();
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
ImagesServlet
處理圖片訪問請求的servlet。只需要將存放的文件以字節流的形式輸出到response中即可。在輸出流之前,我們還要檢查If-Modified-Since和If-None-Match兩個header。如果圖片沒有修改過,則返回304. 如果修改過,則輸出流,并設置新的Last-Modified和ETag兩個header。
public class ImagesServlet extends HttpServlet {
@Override
public void doGet(HttpServletRequest request, HttpServletResponse response) {
String path = request.getRequestURI();
String imageName = path.substring(path.lastIndexOf('/')+1);
File image = new File(request.getServletContext().getInitParameter("shareFolder"), imageName);
if (!image.exists()) {
response.setStatus(404);
return;
}
long lastModified = image.lastModified();
long ifModifiedSince = request.getDateHeader("If-Modified-Since");
if ((lastModified / 1000 * 1000) <= ifModifiedSince) {
response.setStatus(304);
return;
}
response.setStatus(200);
String mimeType = request.getServletContext().getMimeType(imageName);
if(mimeType==null) {mimeType = "application/octet-stream";}
response.addHeader("content-type", mimeType);
response.setContentLength((int)image.length());
response.addDateHeader("Last-Modified", lastModified);
String isDownload = request.getParameter("download");
if (isDownload!=null&&"true".equals(isDownload)) {
//force download
String headerKey = "Content-Disposition";
String headerValue = String.format("attachment; filename=\"%s\"", imageName);
response.setHeader(headerKey, headerValue);
}
FileInputStream in=null;
ServletOutputStream out=null;
try {
in = new FileInputStream(image);
out = response.getOutputStream();
byte[] bytes = new byte[4096];
int bytesReaded = -1;
while ((bytesReaded = in.read(bytes))!=-1) {
out.write(bytes, 0, bytesReaded);
}
out.flush();
} catch (Exception e) {
e.printStackTrace();
response.setStatus(500);
} finally {
//關閉所有的流。
}
}
}
前端開發
前端開發重要是對index.jsp的內容展示方面進行開發。作為網站的welcome頁面,也是唯一的頁面,它將承載所有的用戶交互。在此頁面上只包含兩個功能:
圖片上傳Form。
已經上傳的圖片的展現。
</ol>
index.jsp
使用dojo.io.iframe進行無刷新的圖片上傳。圖片上傳成功以后,會接受服務器返回的JSON對象,其中含有圖片的信息。然后使用JS將圖片插入到所有圖片的前面。
當刷新本頁面時,JSP會掃面圖片文件夾。列出所有的圖片,并將preview顯示在頁面上。我們可以點擊download以下載圖片,也可以點擊view,使得瀏覽器直接打開圖片。
以下是部分代碼。
<script src="http://ajax.googleapis.com/ajax/libs/dojo/1.9.2/dojo/dojo.js"></script>
<script type="text/javascript">
require(["dojo/io/iframe", "dojo/dom-construct","dojo/on","dojo/dom","dojo/query","dojo/string", "dojo/domReady!"],
function(ioIframe,domConst,on,dom,query,string){
var upload=function() {
ioIframe.send({
url:'${pageContext.request.contextPath }/ajaxUploader',
form:'uploader',
handleAs:'json',
load: function(response, ioArgs){
if (response.status == 201) {
var nodeHtml = <!--創建新image的preview的html-->
var node = domConst.toDom(nodeHtml);
domConst.place(node, 'icons', 'first');
} else {
alert(response.message);
}
return response;
},
// Callback on errors:
error: function(response, ioArgs){
alert(response);
return response;
}
});
};
on(dom.byId("uploadButton"), "click", upload);
});
</script>
<div>
<form enctype="multipart/form-data" id="uploader" method="post">
<div><label>Please select a image:</label><input type="file" name="image"/></div>
<div><input type="button" value="Upload" id="uploadButton"></div>
</form>
<div id="icons">
<%
String shareFolder = request.getServletContext().getInitParameter("shareFolder");
File dir = new File(shareFolder);
String[] list = dir.list();
pageContext.setAttribute("list", list);
%>
<c:forEach var="image" items="${list }">
<!--創建每個image的preview-->
</c:forEach>
</div>
</div>
最終的UI效果

源代碼下載地址
http://pan.baidu.com/s/1dDpDNhR
來自:http://my.oschina.net/xpbug/blog/208432