天天看點

基于表單的多檔案上傳

如果在表單中使用表單元素 <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));

}

}

}

}

}