如果在表單中使用表單元素 <input type=“file” />,浏覽器在解析表單時,會自動生成一個輸入框和一個按鈕,輸入框可供使用者填寫本地檔案的檔案名和路徑名,按鈕可以讓浏覽器打開一個檔案選擇框供使用者選擇檔案:
當表單需要上傳檔案時,需指定表單 enctype 的值為 multipart/form-data
在 form 元素的文法中,enctype 屬性指定将資料發送到伺服器時浏覽器使用的編碼類型。
enctype 屬性取值:
application/x-www-form-urlencoded:表單 enctype 屬性的預設值。這種編碼方案使用有限的字元集,當使用了非字母和數字時,必須用”%HH”代替(H 代表十六進制數字)。對于大容量的二進制資料或包含非 ASCII 字元的文本來說,這種編碼不能滿足要求。
multipart/form-data:form 設定了enctype=“multipart/form-data”屬性後,表示表單以二進制傳輸資料 .
Commons-fileupload 元件上傳的基本原理
FileUpload元件将頁面送出的所有元素(普通form表單域,如text和檔案域file)都看作一樣的FileItem,這樣上傳頁面送出的 request請求也就是一個FileItem的有序組合,FileUpload元件可以解析該request,并傳回一個一個的FileItem。而對每一個FileItem,FileUpload元件可以判斷出它是普通form表單域還是檔案file域,進而根據不同的類型,采取不同的操作--如果是表單域,就讀出其值,如果是檔案域,就儲存檔案到伺服器硬碟上或者記憶體中
Commons-fileupload 元件API
ServletFileUpload 負責處理上傳的檔案資料,并将每部分的資料封裝成一到 FileItem 對象中。
DiskFileItemFactory 是建立 FileItem 對象的工廠,在這個工廠類中可以配置記憶體緩沖區大小和存放臨時檔案的目錄。
ServletFileUpload 在接收上傳檔案資料時,會将内容儲存到記憶體緩存區中,如果檔案内容超過了 DiskFileItemFactory 指定的緩沖區的大小,那麼檔案将被儲存到磁盤上,存儲為 DiskFileItemFactory 指定目錄中的臨時檔案。等檔案資料都接收完畢後,ServletUpload 在從檔案中将資料寫入到上傳檔案目錄下的檔案中
進行檔案上傳和下載下傳的具體操作:
首先,加載必要的jar包:“commons-fileupload-1.2.1.jar”,“commons-io-1.4.jar”
步驟:
0. 建立 FileItemFactory 子類 DiskFileItemFactory 的對象
1.為了得到 ServletFileUpload 對象, 先需要得到 FileItemFactory 的一個對象, 然後調用 new ServletFileUpload(FileItemFactory fif); 方法得到 ServletFileUpload 對象
2.為了得到 FileItem 的集合, 先需要得到 ServletFileUpload 對象, 然後調用該對象的 parseRequest() 方法得到 FileItem 的 List
3.從 HttpServletRequest 對象中得到 FileItem 的集合
4. 對上述集合進行周遊:判斷是表單域還是檔案域
下面是對檔案上傳相關操作的代碼示例。
//1. 驗證是否使用 FileUpload 元件
boolean isMultipart = ServletFileUpload.isMultipartContent(request);
//2.1 不使用
if(!isMultipart){
response.getWriter().println("沒有檔案域");
return;
}
//2.2 使用
//3. 擷取 DiskFileItemFactory 對象
DiskFileItemFactory itemFactory = new DiskFileItemFactory();
//4. 擷取 ServletFileUpload 對象
ServletFileUpload fileUpload = new ServletFileUpload();
fileUpload.setFileItemFactory(itemFactory);
設定上傳檔案的最大大小
fileUpload.setFileSizeMax(1000 * 100);
String forwardPage = null;
try {
//5. 調用 ServletFileUpload 的 parseRequest() 方法, 擷取 FileItem 的集合
List<FileItem> items = fileUpload.parseRequest(request);
Map<String, String> paramMap = new HashMap<String, String>();
FileItem fileItem = null;
//6. 對 5 擷取的集合進行周遊
Iterator<FileItem> it = items.iterator();
while(it.hasNext()){
FileItem item = it.next();
//7. 驗證是否為表單域
if(item.isFormField()){
//7.1 是表單域, 把擷取到得 表單域的 fieldName 和 value 放在 map 中.
獲得表單中該字段的字段名
String fieldName = item.getFieldName();
獲得表單中與上面字段名對應的字段值
String value = item.getString("UTF-8");
paramMap.put(fieldName, value);
}
//7.2 不是表單域, 即為檔案域. 擷取 FileItem 對象, 注意限制條件.
else{
這裡獲得了與file對應的FileItem對象
fileItem = item;
}
}
//8. 對 7.2 擷取的 FileItem 對象進行檔案上傳操作.
//10.1 擷取全路徑名
String path = null;
path = this.getServletContext().getRealPath("/photoes");
File photoesDir = new File(path);
if(!photoesDir.exists()){
photoesDir.mkdir();
}
path = path + "/" + paramMap.get("username");
File file = new File(path + ".jpg");
//10.2 上傳操作,把使用者在頁面上傳的檔案儲存到伺服器
fileItem.write(file);
上傳檔案表單的兩個準備工作
1. 送出方式改為 POST
2. enctype 屬性的取值改為 multipart/form-data
在擷取表單值時
1. 不能使用 request.getParameter() 方法, 因為該方法僅能擷取字元串, 對二進制無能為力.
2. 使用 fileUpload 元件讀取上傳檔案及表單域字段.
fileUpload 元件的核心思想
fileUpload 元件認為: 所有的表單域(包含檔案域和文本域)都是 FileItem 對象.
步驟:
1. 擷取 FileItem 對象組成的 List
2. 對 List 進行周遊
3. 判斷每一個 FileItem 對象是表單域 還是 檔案域, 以進行不同的處理.
詳細步驟:
1. 檢查表單的 enctype 是否為 multipart/form-data, 以決定是否使用 FileUpload 元件進行解析請求
2. 若表單的 enctype 是否為 multipart/form-data, 則需要擷取 封裝了所有表單域 的對應的 FileItem 對象的 List: List<FileItem> list = null;
3) 擷取 ServletFileUpload 對象, 調用該對象的 public java.util.List parseRequest(javax.servlet.http.HttpServletRequest request)
方法擷取封裝 FileItem 的List
2) 需要使用 ServletFileUpload 的構造器來建立 ServletFileUpload 對象:
^ 使用任意一個構造器都可以得到 ServletFileUpload 對象, 但都需要先得到 FileItemFactory 對象
* ServletFileUpload upload = new ServletFileUpload();
upload.setFileItemFactory(factory);
* ServletFileUpload upload = new ServletFileUpload(factory);
1) 建立 FileItemFactory 接口是想類對象: DiskFileItemFactory, 直接建立該對象.
3. 可以調用 DiskFileItemFactory 的相關方法來對檔案上傳進行一些優化:
1) setSizeThreshold(int sizeThreshold):設定使用臨時檔案夾的大小.
2) setRepository(): 設定臨時檔案夾
4. 可以調用 ServletUpload 的方法進行檔案上傳的一些控制
1) setFileSizeMax: 設定單個檔案的最大上傳值
2) setSizeMax:設定總上傳檔案的最大值, 以 byte 為機關.
5. 關于中文亂碼的問題:
1) 在解析 request 之前: request.setCharacterEncoding("UTF-8"); 可以解決檔案域的亂碼問題
2) 在解析表單域時需要使用: new String(value.getBytes("iso-8859-1"), "UTF-8"); 解決中文亂碼問題.
但是有時候,即使我們按照上面方式設定了,可以還是中文亂碼,這個時候問題多半就出在tomcat的配置上了,打開tomcat的service.xml檔案,在那裡面注意下面部分
<Connector port="8989" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" useBodyEncodingForURI="true"/>
隻有加上了紅色部分的設定,我們在程式中配置EncodingUrl才能起作用。
下面一段代碼是檔案下載下傳的相關操作
//1. 設定響應報頭 contentType: application/x-msdownload -->
// 告訴浏覽器其所輸出的内容的類型不是普通的文本檔案或 HTML 檔案,而是一個要儲存到本地的下載下傳檔案
//response.setHeader("conent-type", "application/x-msdownload");
response.setContentType("application/x-msdownload");
//2. 設定響應抱頭 Content-Disposition: attachment -->
// Web 伺服器希望浏覽器不直接處理相應的實體内容,而是由使用者選擇将相應的實體内容儲存到一個檔案中
response.setHeader("Content-Disposition", "attachment; filename=haha.ppt");
//3. 利用 response.getOutputStream() 進行 io 操作
OutputStream os = response.getOutputStream();
BufferedOutputStream bos = new BufferedOutputStream(os);
//檔案的輸入流
InputStream is = null;
is = new FileInputStream("E:\\source\\090515\\javaee\\fileupload\\fileUpload.ppt");
BufferedInputStream bis = new BufferedInputStream(is);
int length = 0;
byte [] temp = new byte[1 * 1024 * 10];
while((length = bis.read(temp)) != -1){
bos.write(temp, 0, length);
System.out.println("循環操作中...");
}
bis.close();
bos.close();
但是上面的這種方式無法被迅雷之類的下載下傳工具下載下傳,如果希望被迅雷之類的東西下載下傳,就直接這麼寫就可以了
request.getRequestDispatcher("/data/fileUpload.ppt")
.forward(request, response);
(送出表單時,程式會自動識别表單哪個是檔案,以下自己測試成功的代碼,頁面表單需要做一點點小的設定 enctype="multipart/form-data" method="post",這兩個是必需的)
package com.util;
import java.io.File;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
public class UploadFiles {
public static void uploadFile(HttpServletRequest request, String savePath)
throws Exception {
boolean isMultipart = ServletFileUpload.isMultipartContent(request);
if(!isMultipart){
System.out.println("沒有使用FileUpload元件,請在表單(form)中加入“ enctype=\"multipart/form-data\" method=\"post\" ”");
return;
}
DiskFileItemFactory factory = new DiskFileItemFactory();
factory.setSizeThreshold(10240000);// 設定記憶體緩沖區,超過後寫入臨時檔案
File file = new File(savePath);// 設定臨時檔案存儲位置
if (!file.exists())
file.mkdirs();
factory.setRepository(file);
ServletFileUpload upload = new ServletFileUpload(factory);
upload.setFileSizeMax(10002400000l);// 設定單個檔案的最大上傳值
upload.setSizeMax(10002400000l);// 設定整個request的最大值
upload.setHeaderEncoding("UTF-8");
List<?> items = upload.parseRequest(request);
FileItem item = null;
String fileName = null;
if (items != null) {
for (int i = 0; i < items.size(); i++) {
item = (FileItem) items.get(i);
fileName = savePath + File.separator + item.getName();
// 儲存檔案
if (!item.isFormField() && item.getName().length() > 0) {
item.write(new File(fileName));
}
}
}
}
}