檔案上傳與下載(Commons-fileupload)
檔案上傳與下載
上傳
檔案上傳概述
實現web開發中的檔案上傳功能,需完成如下二步操作:
•在web頁面中新增上傳輸入項
•在servlet中讀取上傳檔案的資料,並儲存到伺服器硬碟中。
如何在web頁面中新增上傳輸入項?
•<input type=“file”>標籤用於在web頁面中新增檔案上傳輸入項,設定檔案上傳輸入項時須注意:
•1、必須要設定input輸入項的name屬性,否則瀏覽器將不會發送上傳檔案的資料。
•2、必須把form的enctype屬值設為multipart/form-data.設定該值後,瀏覽器在上傳檔案時,將把檔案資料附帶在http請求訊息體中,並使用MIME協議對上傳的檔案進行描述,以方便接收方對上傳資料進行解析和處理。
•3 、表單的提交方式要是post
如何在Servlet中讀取檔案上傳資料,並儲存到本地硬碟中?
•Request物件提供了一個getInputStream方法,通過這個方法可以讀取到客戶端提交過來的資料。但由於使用者可能會同時上傳多個檔案,在servlet端程式設計直接讀取上傳資料,並分別解析出相應的檔案資料是一項非常麻煩的工作,示例。
•為方便使用者處理檔案上傳資料,Apache 開源組織提供了一個用來處理表單檔案上傳的一個開源元件( Commons-fileupload ),該元件效能優異,並且其API使用極其簡單,可以讓開發人員輕鬆實現web檔案上傳功能,因此在web開發中實現檔案上傳功能,通常使用Commons-fileupload 元件實現。
使用Commons-fileupload元件實現檔案上傳,需要匯入該元件相應的支撐jar包:Commons-fileupload和commons-io。commons-io 不屬於檔案上傳元件的開發jar檔案,但Commons-fileupload 元件從1.1 版本開始,它工作時需要commons-io包的支援。
檔案上傳步驟
實現步驟
1、建立DiskFileItemFactory物件,設定緩衝區大小和臨時檔案目錄
2、使用DiskFileItemFactory 物件建立ServletFileUpload物件,並設定上傳檔案的大小限制。
3、呼叫ServletFileUpload. parseRequest方法解析request物件,得到一個儲存了所有上傳內容的List物件。
4、對list進行迭代,每迭代一個FileItem物件,呼叫其isFormField方法判斷是否是上傳檔案
•True 為普通表單欄位,則呼叫getFieldName、getString方法得到欄位名和欄位值
•False 為上傳檔案,則呼叫getInputStream方法得到資料輸入流,從而讀取上傳資料。
FileUpload上傳操作核心API
1、DiskFileItemFactory 磁碟檔案項工廠類
public DiskFileItemFactory(int sizeThreshold, java.io.File repository) 構造工廠時,指定記憶體緩衝區大小和臨時檔案存放位置
public void setSizeThreshold(int sizeThreshold) 設定記憶體緩衝區大小,預設10K
public void setRepository(java.io.File repository)設定臨時檔案存放位置,預設System.getProperty("java.io.tmpdir").
記憶體緩衝區: 上傳檔案時,上傳檔案的內容優先儲存在記憶體緩衝區中,當上傳檔案大小超過緩衝區大小,就會在伺服器端產生臨時檔案
臨時檔案存放位置: 儲存超過了記憶體緩衝區大小上傳檔案而產生臨時檔案
* 產生臨時檔案可以通過 FileItem的delete方法刪除
2、ServletFileUpload 檔案上傳核心類
static boolean isMultipartContent(javax.servlet.http.HttpServletRequest request) 判斷request的編碼方式是否為multipart/form-data
java.util.List parseRequest(javax.servlet.http.HttpServletRequest request) 解析request,將請求體每個部分封裝FileItem物件,返回List<FileItem>
void setFileSizeMax(long fileSizeMax) 設定單個檔案上傳大小
void setSizeMax(long sizeMax) 設定總檔案上傳大小
void setHeaderEncoding(java.lang.String encoding) 設定編碼集 解決上傳檔名亂碼 *****
3、FileItem 表示檔案上傳表單中 每個資料部分
boolean isFormField() 判斷該資料項是否為檔案上傳項,true 不是檔案上傳 false 是檔案上傳
if(fileItem.isFormField()){
// 不是上傳項
java.lang.String getFieldName() 獲得普通表單項name屬性
java.lang.String getString() / java.lang.String getString(java.lang.String encoding) 獲得普通表單項value屬性 傳入編碼集用來解決輸入value亂碼
}else{
// 是上傳項
java.lang.String getName() 獲得上傳檔名 (注意IE6存在路徑)
java.io.InputStream getInputStream() 獲得上傳檔案內容輸入流
// 上傳檔案
void delete() 刪除臨時檔案(刪除時,必須要管理輸入輸出流)
}
注意事項:因為檔案上傳表單採用編碼方式multipart/form-data 與傳統url編碼不同,所有getParameter 方法不能使用 setCharacterEncoding 無法解決輸入項亂碼問題
JavaScript的多檔案上傳表單
技巧:
•每次動態增加一個檔案上傳輸入框,都把它和刪除按紐放置在一個單獨的div中,並對刪除按紐的onclick事件進行響應,使之刪除刪除按紐所在的div。
•如:
this.parentNode.parentNode.removeChild(this.parentNode);
上傳檔案存在的問題
上傳檔案後,在伺服器端儲存位置
第一類存放位置:直接存放WebRoot目錄下 和 除WEB-INF META-INF的其它子目錄下 例如: WebRoot/upload
* 客戶端可以直接在瀏覽器上通過url訪問位置(資料無需通過許可權控制,而可以直接訪問) —- 對上傳資源安全性要求不高、或者資源需要使用者直接可見
* 例如:購物商城商品圖片
第二類存放位置:放入WEB-INF及其子目錄 或者 不受tomcat伺服器管理目錄 例如: WebRoot/WEB-INF/upload 、c:\ 、d:\abc
* 客戶端無法通過URL直接訪問,必須由伺服器內部程式才能讀取 (安全性較高,可以很容易新增許可權控制)
* 例如:會員制線上視訊
上傳檔案在同一個目錄重名問題
如果檔案重名,後上傳檔案就會覆蓋先上傳檔案
檔名 UUID
filename = UUID.randomUUID().toString() + "_" + filename;
為了防止同一個目錄下方上傳檔案數量過多 —- 必須採用目錄分離演算法
1) 按照上傳時間進行目錄分離 (周、月 )
2) 按照上傳使用者進行目錄分離 —– 為每個使用者建立單獨目錄
3) 按照固定數量進行目錄分離 —— 假設每個目錄只能存放3000個檔案 ,每當一個目錄存滿3000個檔案後,建立一個新的目錄
4) 按照唯一檔名的hashcode 進行目錄分離
public static String generateRandomDir(String uuidFileName) {
// 獲得唯一檔名的hashcode
int hashcode = uuidFileName.hashCode();
// 獲得一級目錄
int d1 = hashcode & 0xf;
// 獲得二級目錄
int d2 = (hashcode >>> 4) & 0xf;
return "/" + d2 + "/" + d1;// 共有256目錄l
}
亂碼問題
普通編寫項 value屬性亂碼 ————- fileItem.getString(編碼集);
上傳檔案項 檔名亂碼 ——— fileupload.setHeaderEncoding(編碼集);
下載
常見檔案下載有兩種方式
1、超連結直接指向下載資源
如果檔案格式瀏覽器識別,將直接開啟檔案,顯示在瀏覽器上, 如果檔案格式瀏覽器不識別,將彈出下載視窗
對於瀏覽器識別格式的檔案,通過另存為進行下載
客戶端訪問伺服器靜態資原始檔時,靜態資原始檔是通過 預設Servlet返回的,在tomcat配置檔案conf/web.xml 找到 — org.apache.catalina.servlets.DefaultServlet
2、編寫伺服器程式,讀取伺服器端檔案,完成下載
必須設定兩個頭資訊 ,來自MIME協議 Content-Type Content-Disposition
response.setContentType(getServletContext().getMimeType(filename));
response.setHeader("Content-Disposition", "attachment;filename=" + filename); // 以附件形式開啟,不管格式瀏覽器是否識別
處理IE瀏覽器與Firefox瀏覽器亂碼問題
if (agent.contains("MSIE")) {
// IE瀏覽器
filename = URLEncoder.encode(filename, "utf-8");
filename = filename.replace("+", " ");
} else if (agent.contains("Firefox")) {
// 火狐瀏覽器
BASE64Encoder base64Encoder = new BASE64Encoder();
filename = "=?utf-8?B?"
+ base64Encoder.encode(filename.getBytes("utf-8"))
+ "?=";
} else if (agent.contains("Chrome")) {
// google瀏覽器
filename = URLEncoder.encode(filename, "utf-8");
} else {
// 其它瀏覽器
filename = URLEncoder.encode(filename, "utf-8");
}
綜合案例 網盤系統
需求:
1、系統提供一個檔案上傳功能,在使用者上傳檔案後,檔案儲存在伺服器端指定目錄,檔案相關資訊儲存在資料庫中
* 每上傳一個檔案,資料庫中存在一條資料記錄
2、系統提供一個檔案下載功能,將資料表中所有資源資訊,顯示在頁面上,允許使用者進行下載
建立資料庫環境
create database day23
create table resources(
id int primary key auto_increment,
uuidname varchar(100) unique not null,
realname varchar(40) not null,
savepath varchar(100) not null,
uploadtime timestamp ,
description varchar(255)
);
匯入jar包 、c3p0-config.xml 、JDBCUtils工具類