原因
1. 上傳文件,方法一定要用post
2. 上傳文件僅上傳文件名,不上傳文件的內容,
需要設置enctype屬性 enctype="multipart/form-data",這時上傳的文件中還包含一些不屬於文件的東西,需要分割
3. 這個時候如果還上傳了普通的表單數據,會和文件資源一起被保存到文件中
4. request.getInputStream()獲得是整個請求體的內容 ,需要將不屬於文件的內容分出去
此時,request獲取請求參數的API也不能用了,即request.getPramaer(name)不可用
5. 要使用專門的文件上傳組件
文件上傳組件
依賴包
Commons-fileupload和commons-io
實現步驟
1、創建DiskFileItemFactory對象,設置緩衝區大小和臨時文件目錄
//工廠約束
public DiskFileItemFactory(int sizeThreshold, java.io.File repository) //構造函數
public void setSizeThreshold(int sizeThreshold) //設置內存緩衝區的大小,默認值爲10K。當上傳文件大於緩衝區大小時, fileupload組件將使用臨時文件緩存上傳文件。
public void setRepository(java.io.File repository) //指定臨時文件目錄,默認值爲System.getProperty("java.io.tmpdir").
factory.setSizeThreshold(yourMaxMemorySize);
factory.setRepository(yourTempDirectory);
2、使用DiskFileItemFactory 對象創建ServletFileUpload對象,並設置上傳文件的大小限制。
List parseRequest(HttpServletRequest request)//解析request對象,並把表單中的每一個輸入項包裝成一個fileItem 對象,並返回一個保存了所有FileItem的list集合。
boolean isMultipartContent(HttpServletRequest request) //判斷上傳表單是否爲multipart/form-data類型
//上傳文件大小約束
setFileSizeMax(long fileSizeMax)//設置單個上傳文件的最大值bytes
setSizeMax(long sizeMax) //設置上傳文件總量的最大值(多個文件同時上傳時文件最大值的總和)
setHeaderEncoding(java.lang.String encoding)//設置編碼格式,解決上傳文件名亂碼問題
3、調用ServletFileUpload.parseRequest方法解析request對象,得到一個保存了所有上傳內容的List對象。
boolean isFormField() //判斷FileItem是一個文件上傳對象還是普通表單對象
1.如果判斷是一個普通表單對象
String getFieldName() //獲得普通表單對象的name屬性
String getString(String encoding) //獲得普通表單對象的value屬性
2.如果判斷是一個文件上傳對象
String getName() //獲得上傳文件的文件名(有些瀏覽器會攜帶客戶端路徑)
InputStream getInputStream() //獲得上傳文件的輸入流
delete() //在關閉FileItem輸入流後,刪除臨時文件
4、對list進行迭代,每迭代一個FileItem對象,調用其isFormField方法判斷是否是上傳文件
True 爲普通表單字段,則調用getFieldName、getString方法得到字段名和字段值
False 爲上傳文件,則調用getInputStream方法得到數據輸入流,從而讀取上傳數據。
//檢查我們是否有文件上傳請求
boolean isMultipart = ServletFileUpload.isMultipartContent(request);
示例:
1.創建一個工廠
DiskFileItemFactory factory = new DiskFileItemFactory();
2.配置存儲文件的地址庫
ServletContext servletContext = this.getServletConfig().getServletContext();
File repository = (File) servletContext.getAttribute("javax.servlet.context.tempdir");
factory.setRepository(repository);
3.創建ServletFileUpload對象,用於解析request
ServletFileUpload upload = new ServletFileUpload(factory);
4.解析request
List<FileItem> items = upload.parseRequest(request);
5.遍歷上傳的文件
Iterator<FileItem> iter = items.iterator();
while(iter.hasNext()){
FileItem item = iter.next();
if(item.isFormField){
processFormField(item);//處理表單
}else{
processUploadFile(item);//處理上傳文件
}
}
6.處理表單
String name = item.getFieldName();//獲取表單key
String value = item.getString("utf-8");//獲取表單的值,可加參數解決表單數據中文亂碼問題
7.處理文件上傳
String fieldName = item.getFieldName();
String fileName = item.getName();
contentType = item.getContentType();
boolean isInMemory = item.isInMemory();
long sizeInBytes = item.getSize();
8.將文件存入磁盤
String upload = getServletContext().getRealPath("upload");
File file = new File(upload + "/" + fileName);
item.write(file);
細節
1.上傳文件重名問題
//文件名稱需要做一個處理,保證每一個文件不會與其他文件重名
方式一:用戶的id -_ fileName
方式二:當前的時間戳加上一些序列數2019082411254790
方式三:加隨機數uuid
String uuid = UUID.randomUUID().toString();
fileName = uuid + "-" + fileName;
2.文件數目過多
//使用散列分散文件
String upload = getServletContext().getRealPath("upload");
int hashCode = fileName.hashCode();
//int 32 16 8 1 2 3 4 a b f 3 16^8 40億左右
String s = Integer.toHexString(hashCode);//轉成16進制,八個文件夾
char[] chars = s.toCharArray();
for (char aChar : chars) {
upload = upload + "/" + aChar;
}
File file = new File(upload + "/" + fileName);