1. 程式人生 > >檔案上傳與下載(Commons-fileupload)

檔案上傳與下載(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工具類