文件上傳和下載
一、文件的上傳和下載
1、文件上傳的原理分析
1.1文件上傳的必要前提:
a、提供form表單,method必須是post
b、form表單的enctype必須是multipart/form-data
c、提供input type="file"類的上傳輸入域
1.2enctype屬性
作用:告知服務器請求正文的MIME類型。(請求消息頭:Content-Type作用是一致的)
可選值:
application/x-www-form-urlencoded(默認):
正文:name=admin&password=123
服務器獲取數據:String name = request.getParameter("name");
multipart/form-data:
正文:對每個域分開上傳
服務器獲取數據:request.getParameter(String)方法獲取指定的表單字段字符內容,
但文件上傳表單已經不在是字符內容,而是字節內容,所以失效。
所以使用request.getInputStream()獲取數據
文件上傳:解析請求正文的每部分的內容。
2、借助第三方的上傳組件實現文件上傳
2.1 fileupload概述
fileupload是由apache的commons組件提供的上傳組件。它最主要的工作就是幫我們解析request.getInputStream()。
導入commons-fileupload相關jar包
commons-fileupload.jar,核心包;
commons-io.jar,依賴包。
2.2 fileupload的核心類有:
DiskFileItemFactory、ServletFileUpload、FileItem。
2.3 fileupload簡單應用
使用fileupload組件的步驟如下:
1. 創建工廠類DiskFileItemFactory對象:
DiskFileItemFactory factory = new DiskFileItemFactory()
2. 使用工廠創建解析器對象:
ServletFileUpload fileUpload = new ServletFileUpload(factory)
3. 使用解析器來解析request對象:
List<FileItem> list = fileUpload.parseRequest(request)
FileItem對象對應一個表單項(表單字段)。可以是文件字段或普通字段
boolean isFormField():判斷當前表單字段是否為普通文本字段,如果返回false,說明是文件字段;
String getFieldName():獲取字段名稱,例如:<input type=”text” name=”username”/>,返回的是username;
String getString():獲取字段的內容,如果是文件字段,那麽獲取的是文件內容,當然上傳的文件必須是文本文件;
String getName():獲取文件字段的文件名稱;(a.txt)
String getContentType():獲取上傳的文件的MIME類型,例如:text/plain。
int getSize():獲取上傳文件的大小;
InputStream getInputStream():獲取上傳文件對應的輸入流;
void write(File):把上傳的文件保存到指定文件中。
delete(): 刪除在上傳大文件時產生的臨時存儲文件。
3、文件上傳時要考慮的幾個問題(經驗分享)
a、解決獲得客戶端文件名時可能帶有全路徑的問題(造成服務端存盤有雙根目錄)
filename = FilenameUtils.getName(filename);
a、保證服務器的安全
把保存上傳文件的目錄放在用戶直接訪問不到的地方。
String directoryRealPath = this.getServletContext().getRealPath("/WEB-INF/upload");
b、避免文件被覆蓋
讓文件名唯一即可
filename = UUID.randomUUID() + "_" + filename; (或者)
filename.substring(filename.lastIndexOf(File.separator)+1);
c、避免同一個文件夾中的文件過多
方案一:按照日期進行打散存儲目錄
private String makeChildDirectory(File storeDirectory) {
SimpleDateFormat sdf =new SimpleDateFormat("yyyy-MM-dd");
String dateDirectory = sdf.format(new Date());
//只管創建目錄 File file = new File(storeDirectory,dateDirectory);
if(!file.exists()){ file.mkdirs(); }
return dateDirectory;
}
方案二:用文件名的hashCode計算打散的存儲目錄:二級目錄
private String makeChildDirectory(File storeDirectory, String filename) {
int hashcode = filename.hashCode();// 返回字符轉換的32位hashcode碼
System.out.println(hashcode);
String code = Integer.toHexString(hashcode); // 把hashcode轉換為16進制的字符
// abdsaf2131safsd
System.out.println(code);
String childDirectory = code.charAt(0) + File.separator+ code.charAt(1); // a/b
// 創建指定目錄
File file = new File(storeDirectory, childDirectory);
if (!file.exists()) {
file.mkdirs();
}
return childDirectory;
}
d、限制文件的大小:web方式不適合上傳大的文件
單個文件大小:
ServletFileUpload.setFileSizeMax(字節)
總文件大小:(多文件上傳)
ServletFileUpload.setSizeMax(字節)
e、上傳字段用戶沒有上傳的問題
通過判斷文件名是否為空即可
f、臨時文件的問題
DiskFileItemFactory:
作用:產生FileItem對象
內部有一個緩存,緩存大小默認是10Kb。如果上傳的文件超過10Kb,用磁盤作為緩存。
存放緩存文件的目錄在哪裏?默認是系統的臨時目錄。
如果自己用IO流實現的文件上傳,要在流關閉後,清理臨時文件。
FileItem.delete();
文件上傳和下載