一、簡單介紹
使用 Servlet上傳附件 原理上還是蠻簡單的,首先擷取上傳的附件對象,然後做一些簡單處理 後寫入到指定路徑的磁盤中。
二、準備條件
common-io.jar ,下載下傳位址:http://commons.apache.org/io/download_io.cgi
common-upload.jar ,下載下傳位址:http://jakarta.apache.org/commons/fileupload/
三、實作流程
首先在建立好的項目中 加入上述的兩個必須jar包,然後根據需求建立好upload.jsp頁面選擇附件,代碼如下:
<%@page import="java.io.File"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Use servlet upload file</title>
</head>
<body>
<form name="uploadForm" method="post" action="servlet/uploadFileServlet" enctype="multipart/form-data">
附件名稱:<input type="text" name="uploadName" value=""/><br/>
選擇附件:<input type="file" name="uploadFile"/><br/>
<input type="submit" value="上傳"/>
</form>
</body>
</html>
其中,enctype="multipart/form-data" 是必須加入的,因為原先沒有加入的普通表單是以字元送出到背景的,但是加了之後所有參數全部會以二進制流格式傳入背景,是以背景處理代碼 request.getParameter("paramName") 将傳回空字元串。
基本顯示如下圖:
頁面定義好了,接下來建立上面送出的servlet ,代碼如下:
package com.chenghui.servlet;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Iterator;
import java.util.List;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
/**
* Servlet implementation class UploadFileServlet
*/
public class UploadFileServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private static final String UPLOAD_PATH = "d:\\attach\\";
/**
* @see HttpServlet#HttpServlet()
*/
public UploadFileServlet() {
super();
// TODO Auto-generated constructor stub
}
/**
* @see Servlet#init(ServletConfig)
*/
public void init(ServletConfig config) throws ServletException {
// TODO Auto-generated method stub
}
/**
* @see Servlet#destroy()
*/
public void destroy() {
// TODO Auto-generated method stub
}
/**
*
* 偶然發現一個現象,當我定義的servlet 中重寫了 service方法,那麼每次調用的是service方法,除非注釋掉該方法才會根據請求挑戰到對象的doPost/doGet中
* @see HttpServlet#service(HttpServletRequest request, HttpServletResponse response)
*//*
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("service");
}*/
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//建立一個磁盤檔案的工廠,然後将它 傳遞到servletFileUplaod的執行個體中
DiskFileItemFactory diskFileItemFactory = new DiskFileItemFactory();
ServletFileUpload servletFileUpload = new ServletFileUpload(diskFileItemFactory);
try {
//根據request對象擷取所有的檔案集合,這裡包括input标簽輸入的值也屬于FileInput
List<FileItem> fileItemList = servletFileUpload.parseRequest(request);
Iterator iterator = fileItemList.iterator();
String showFileName = "";
while(iterator.hasNext()){
FileItem fileItem = (FileItem)iterator.next();
if(fileItem.isFormField()){ //是否是表單送出域,可以分區是否上傳的附件
String name = fileItem.getFieldName(); //input标簽的name
String value = fileItem.getString(); //input表單的value
if("uploadName".equals(name)){
showFileName = value;
}
}else{
String fieldName = fileItem.getFieldName(); //表單送出過來的file input标簽中name的屬性值
String fileName = fileItem.getName(); //file input上傳的檔案名
String contentType = fileItem.getContentType(); //獲得上傳檔案的類型
long size = fileItem.getSize(); //上傳檔案的大小
String filePath = UPLOAD_PATH + showFileName + fileName.substring(fileName.lastIndexOf("."));
File saveFile = new File(filePath);
fileItem.write(saveFile); //将檔案寫入磁盤中
//成功之後 簡單輸出一下
PrintWriter out = response.getWriter();
out.print("上傳成功!上傳檔案為:"+fileName+"<br/>儲存的位址為"+filePath+ "!!");
out.close();
}
}
} catch (FileUploadException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
在web.xml中對應的配置:
<servlet>
<servlet-name>uploadFileServlet</servlet-name>
<servlet-class>com.chenghui.servlet.UploadFileServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>uploadFileServlet</servlet-name>
<url-pattern>/servlet/uploadFileServlet</url-pattern>
</servlet-mapping>
好了基本上可以走一下流程了,在upload.jsp頁面選擇附件後點選“上傳”按鈕,效果顯示如下
然後去d:attach 目錄下找上傳的附件,找到了,ok上傳成功!但是顯示的文字有亂碼,這是因為response對象在輸出的時候沒有指定具體的編碼,是以按照預設的iso-8859-1輸出 就亂碼了。
解決方案:在 PrintWriter out = response.getWriter(); 的上面加上
response.setContentType("text/html;charset=UTF-8");
就好了。
另外,将上傳的附件是使用org.apache.commons.fileupload.FileItem 提供write方法寫入磁盤中的,這也可以用位元組流完成該操作。
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//建立一個磁盤檔案的工廠,然後将它 傳遞到servletFileUplaod的執行個體中
DiskFileItemFactory diskFileItemFactory = new DiskFileItemFactory();
ServletFileUpload servletFileUpload = new ServletFileUpload(diskFileItemFactory);
try {
//根據request對象擷取所有的檔案集合,這裡包括input标簽輸入的值也屬于FileInput
List<FileItem> fileItemList = servletFileUpload.parseRequest(request);
Iterator iterator = fileItemList.iterator();
String showFileName = "";
//如果附件位址不存在 就建立一下
File uploadPath = new File(UPLOAD_PATH);
if(!uploadPath.exists()){
uploadPath.mkdir();
}
while(iterator.hasNext()){
FileItem fileItem = (FileItem)iterator.next();
if(fileItem.isFormField()){ //是否是表單送出域,可以分區是否上傳的附件
String name = fileItem.getFieldName(); //input标簽的name
String value = fileItem.getString(); //input表單的value
if("uploadName".equals(name)){
showFileName = value;
}
}else{
String fieldName = fileItem.getFieldName(); //表單送出過來的file input标簽中name的屬性值
String fileName = fileItem.getName(); //file input上傳的檔案名
String contentType = fileItem.getContentType(); //獲得上傳檔案的類型
long size = fileItem.getSize(); //上傳檔案的笑答
String filePath = UPLOAD_PATH + showFileName + fileName.substring(fileName.lastIndexOf("."));
//org.apache.commons.fileupload.FileItem 提供write方法寫入磁盤中
//fileItem.write(new File(filePath));
//使用位元組流讀取二進制格式的附件傳給檔案流 然後 寫入磁盤
OutputStream outputStream = new FileOutputStream(new File(UPLOAD_PATH,showFileName + fileName.substring(fileName.lastIndexOf("."))));//這裡傳遞父親的檔案夾路徑和目前檔案的名稱
InputStream inputStream = fileItem.getInputStream();
int length = 0;
byte[] buf = new byte[1024];
while((length = inputStream.read(buf)) != -1){ //首先根據傳遞的位元組數組将讀取的位元組的數量傳回,在判斷是否讀取的空
System.out.println(buf);
outputStream.write(buf, 0, length);
}
inputStream.close();
outputStream.close();
//成功之後 簡單輸出一下
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
out.print("上傳成功!上傳檔案為:"+fileName+"<br/>儲存的位址為"+filePath+ "!!");
out.close();
}
}
} catch (FileUploadException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
上傳的效果是一樣的。輸出每次讀取的byte數組,效果如下:
基本上,使用servlet 上傳附件的功能就已經完成了。
四、多附件上傳
如果有需求需要上傳多個附件,那麼現在這個demo同樣有效果,隻需要稍作修改就好了。
upload.jsp需要增加一個選擇附件的控件,代碼 如下:
<%@page import="java.io.File"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Use servlet upload file</title>
</head>
<body>
<form name="uploadForm" method="post" action="servlet/uploadFileServlet" enctype="multipart/form-data">
附件名稱:<input type="text" name="uploadName" value=""/><br/>
選擇附件:<input type="file" name="uploadFile"/><br/>
附件名稱:<input type="text" name="uploadName1" value=""/><br/>
選擇附件:<input type="file" name="uploadFile2"/><br/>
<input type="submit" value="上傳"/>
</form>
</body>
</html>
servlet.java 邏輯其實不用改,因為我們是疊代所有FileItem對象的集合,是以所有的附件和附件資訊全都儲存在這個集合中,簡單提煉了下代碼,具體如下:
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
//建立一個磁盤檔案的工廠,然後将它 傳遞到servletFileUplaod的執行個體中
DiskFileItemFactory diskFileItemFactory = new DiskFileItemFactory();
ServletFileUpload servletFileUpload = new ServletFileUpload(diskFileItemFactory);
try {
//根據request對象擷取所有的檔案集合,這裡包括input标簽輸入的值也屬于FileInput
List<FileItem> fileItemList = servletFileUpload.parseRequest(request);
Iterator iterator = fileItemList.iterator();
String showFileName = "";
String showFileName1 = "";
//如果附件位址不存在 就建立一下
File uploadPath = new File(UPLOAD_PATH);
if(!uploadPath.exists()){
uploadPath.mkdir();
}
while(iterator.hasNext()){
FileItem fileItem = (FileItem)iterator.next();
if(fileItem.isFormField()){ //是否是表單送出域,可以分區是否上傳的附件
String name = fileItem.getFieldName(); //input标簽的name
String value = fileItem.getString(); //input表單的value
showFileName = value; //這裡注意好出場順序,不然就亂套了
}else{
String fieldName = fileItem.getFieldName(); //表單送出過來的file input标簽中name的屬性值
String fileName = fileItem.getName(); //file input上傳的檔案名
String contentType = fileItem.getContentType(); //獲得上傳檔案的類型
long size = fileItem.getSize(); //上傳檔案的笑答
String filePath = UPLOAD_PATH + showFileName + fileName.substring(fileName.lastIndexOf("."));
//org.apache.commons.fileupload.FileItem 提供write方法寫入磁盤中
//fileItem.write(new File(filePath));
//使用位元組流讀取二進制格式的附件傳給檔案流 然後 寫入磁盤
OutputStream outputStream = new FileOutputStream(new File(UPLOAD_PATH,showFileName + fileName.substring(fileName.lastIndexOf("."))));//這裡傳遞父親的檔案夾路徑和目前檔案的名稱
InputStream inputStream = fileItem.getInputStream();
int length = 0;
byte[] buf = new byte[1024];
while((length = inputStream.read(buf)) != -1){ //首先根據傳遞的位元組數組将讀取的位元組的數量傳回,在判斷是否讀取的空
System.out.println(buf);
outputStream.write(buf, 0, length);
}
inputStream.close();
outputStream.close();
//成功之後 簡單輸出一下
out.print("上傳成功!上傳檔案為:"+fileName+"<br/>儲存的位址為"+filePath+ "!!");
}
}
} catch (FileUploadException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}finally{
out.close();
}
}
重新部署項目後運作,選擇好附件,然後送出後檢視源代碼如下:
這裡發現沒有換行,是以我在servlet 中使用out.println() 輸出發現頁面依舊沒有換行,隻不過源代碼換行了,具體如下:
這樣 源代碼換行了,頁面顯示也換行了。
五、實作下載下傳功能
上傳功能實作完了 之後再來說說下載下傳功能的具體實作。
如果我們把附件放在目前項目下,可以直接通過超連結 進行下載下傳,但是假如附件放在我們上面例子的d:\attach 下那怎麼辦,tomcat 是不可能通路項目以外的資源的。
是以接下來有兩種解決方案:
1、建立一個虛拟目錄
我們釋出項目的時候,也是通過在server.xml配置<Context> 實作将一個虛拟目錄釋出到tomcat伺服器上,不一定這個虛拟目錄上非得有什麼WEB-INF,web.ml 等等之類的檔案,這個目錄專門存放一些附件之類的同樣可以通路。
那麼同理 在server.xml 中加上如下例子:
<Context docBase="D:\attach" path="/img" reloabable="true" />通路的話可以使用 localhost:8080/img/imgName 就好了。
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//擷取需要下載下傳的附件的位址,然後采用輸入流InputStream讀取附件資訊
String filePath = request.getParameter("filePath");
if(filePath!=null){
try{
File file = new File(filePath);
//建立輸入流
InputStream inputStream = new BufferedInputStream(new FileInputStream(file));
byte[] buffer = new byte[inputStream.available()];
//傳遞available() 有效的位元組數,可以一次性讀取完畢
inputStream.read(buffer);
inputStream.close();
//清空response,設定response的Header
response.reset();
response.addHeader("Content-Disposition", "attachment;filename=" + new String(file.getName().getBytes("utf-8"),"ISO-8859-1"));
response.addHeader("Content-Length", "" + file.length());
OutputStream outputStream = new BufferedOutputStream(response.getOutputStream());
response.setContentType("application/octet-stream");
outputStream.write(buffer);
outputStream.flush();
outputStream.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
然後把上傳的輸出字元串改一下,加一個超連結,代碼如下:
//成功之後 簡單輸出一下
out.println("上傳成功!上傳檔案為:"+fileName+"<br/>儲存的位址為"+filePath+ "!!<a href='downloadFileServlet?filePath="+filePath+"'>下載下傳</a><br/>");
接下來 重新部署下項目,看看結果。效果如下圖所示:
ok,下載下傳附件功能也完成了!
六、上傳附件中遇到問題的解決方案
1、中文亂碼
在上面的例子中 附件顯示名稱和 附件的名稱都是使用的英文名稱,如果改為中文呢? 是以果斷嘗試了一下,結果都亂碼了,當時第一時間想到的是 使用request.setCharacterEncoding("UTF-8"); 但是沒有效果呀,這種設定字元編碼 方式隻适合普通表單的post請求。
後來發現 可以這樣,在建立ServletFileUpload對象之後設定一下頭部編碼,代碼如下:
ServletFileUpload servletFileUpload = new ServletFileUpload(diskFileItemFactory);
servletFileUpload.setHeaderEncoding("UTF-8");
再試一下,發現 附件的實際名稱沒變亂碼,但是 附件的顯示名稱還是亂碼,這需要單獨設定encoding,原先取參數都是
String value = fileItem.getString(); //input表單的value
需要傳遞encoding ,讓FileItem知道你想得到什麼格式的value,代碼如下:
String value = fileItem.getString("UTF-8"); //input表單的value
再重新部署一下,具體效果如下:
位址:http://blog.csdn.net/chenghui0317/article/details/9502143