在WEB頁面中添加上傳輸入項,<input type=“life” name=“”>,使用時注意:
1. 必須要設置input輸入項的name屬性,否則瀏覽器將不會發送上傳文件的數據。
2. 必須把input項的enctype屬值設爲multipart/form-data.設置該值後,瀏覽器在上傳文件時,將把文件數據附帶在http請求消息體中,並使用MIME協議對上傳的文件進行描述,以方便接收方對上傳數據進行解析和處理。
如何在Servlet中讀取文件上傳數據,並保存到服務器本地硬盤中?
Request對象提供了一個getInputStream()方法,使用這個方法可以獲取瀏覽器提交過來的輸入流。但如果瀏覽器上傳多個文件時,我們應該如何區分開來?這是一項複雜的工作。
爲了方便用戶處理文件上傳數據,Apache開源組織提供了用於處理上面應用的開源組件——Commons-fileupload。這個組件使用簡單,性能也比較優異,所以幾乎都使用它來實現上傳文件的處理功能。
DiskFileItemFactory是創建FileItem對象的工廠,這個工廠常用方法:
1. public DiskFileItemFactory(int sizeThreshold, java.io.File repository),常用的構造函數。
2. public void setSizeThreshold(int sizeThreshold),設置內存緩衝區的大小,默認值爲10K。當上傳文件大於緩衝區大小時, fileupload組件將使用臨時文件緩存上傳文件。
3. public void setRepository(java.io.File repository),指定臨時文件目錄,默認值爲System.getProperty("java.io.tmpdir")。
ServletFileUpload 負責處理上傳的文件數據,並將表單中每個輸入項封裝到一個FileItem對象中。常用方法有:
1. boolean isMultipartContent(HttpServletRequest request),判斷上傳表單是否爲上傳表單類型。
2. List parseRequest(HttpServletRequest request),解析request對象,並把表單中的每一個輸入項包裝到一個fileItem 對象中,並返回一個保存了所有FileItem的list集合。
3. setFileSizeMax(long fileSizeMax),設置上傳文件的最大尺寸值。
4. setSizeMax(long sizeMax),設置上傳文件總量的最大值。
5. setHeaderEncoding(java.lang.String encoding),設置編碼格式。如果文件路徑中存在中文可能會造成文件路徑亂碼,用此方法處理可以解決。
上傳文件案例:
- public class FileuploadServlet extends HttpServlet {
- public void dopost(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
- // 創建文件處理工廠,它用於生成FileItem對象。
- DiskFileItemFactory difactory = new DiskFileItemFactory();
- //設置緩存大小,如果上傳文件超過緩存大小,將使用臨時目錄做爲緩存。
- difactory.setSizeThreshold(1024 * 1024);
- // 設置處理工廠緩存的臨時目錄,此目錄下的文件需要手動刪除。
- String dir = this.getServletContext().getRealPath("/");
- File filedir = new File(dir + "filetemp");
- if (!filedir.exists())
- filedir.mkdir();
- difactory.setRepository(filedir);
- // 設置文件實際保存的目錄
- String userdir = dir + "files";
- File fudir = new File(userdir);
- if (!fudir.exists())
- fudir.mkdir();
- // 創建request的解析器,它會將數據封裝到FileItem對象中。
- ServletFileUpload sfu = new ServletFileUpload(difactory);
- // 解析保存在request中的數據並返回list集合
- List list = null;
- try {
- list = sfu.parseRequest(request);
- } catch (FileUploadException e) {
- e.printStackTrace();
- }
- // 遍歷list集合,取出每一個輸入項的FileItem對象,並分別獲取數據
- for (Iterator it = list.iterator(); it.hasNext();) {
- FileItem fi = (FileItem) it.next();
- if (fi.isFormField()) {
- System.out.println(fi.getFieldName());
- System.out.println(fi.getString());
- } else {
- //由於客戶端向服務器發送的文件是客戶端的全路徑,在這我們只需要文件名即可
- String fifilename = fi.getName();
- int index = filename.lastIndexOf("\\");
- if(index != -1)
- filenamefilename = filename.substring(index+1);
- //向服務器寫出文件
- InputStream in = fi.getInputStream();
- FileOutputStream fos = new FileOutputStream(fudir + "/" +filename);
- byte[] buf = new byte[1024];
- int len = -1;
- while((len = in.read(buf)) != -1){
- fos.write(buf, 0, len);
- }
- // 關閉流
- if(in != null){
- try{
- in.close();
- }finally{
- if(fos!=null)
- fos.close();
- }
- }
- }
- }
- }
- public void doGet(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
- doPost(request, response);
- }
- }
上傳文件的處理細節(1):
1. 中文文件亂碼的問題,可以調用兩個方法來設置字符編碼:servletUpLoader.setHeaderEncoding()或request.setCharacterEncoding()。我們可以在源文件的創建ServletFileUpload對象後邊添加如下代碼:
sfu.setHeaderEncoding("UTF-8");
2. 臨時文件的刪除,如果臨時文件大於setSizeThreshold設置的緩存大小,Commons-fileupload組件將使用setRepository設置的臨時目錄來保存上傳的文件,上傳完成後我們需要手動調用FileItem.delete來刪除臨時文件。建議不要修改緩存區大小,如果設置緩存爲1MB,1000個用戶上傳文件就需要1000MB內存,服務器會受不了的。我們刪除掉setSizeThreshold的代碼,並在每次完成一個文件後添加下而的代碼:
// 刪除臨時目錄中的文件
fi.delete();
上傳文件的處理細節(2):
1. 在上面的代碼中,我們將文件的實際保存目錄設置在WEB-INF目錄之外。這樣外部可以直接訪問被上傳的文件,這會造成安全問題。比如用戶上傳了一個帶有惡意腳本功能的JSP文件,然後從外部訪問執行了JSP文件…後果不堪設想。所以我們將源代碼中對應位置處修改如下:
// 之所以放在"WEB-INF"目錄下是爲了防止上傳的文件被直接被訪問的安全問題
String userdir = dir + "WEB-INF/files";
2. 一個WEB應用會許多不同的用戶訪問,不同的用戶可能會上傳相同名稱的文件,如果這樣可能會造成文件覆蓋的情況發生,所以我們必須保證文件名稱的唯一性,我編寫一個方法來生成唯一性名稱的文件名:
/**
* 生成具有唯一性的UUID文件名稱
* @param fileName
* @return
*/
private String uuidName(String fileName){
UUID uuid = UUID.randomUUID();
return uuid.toString() + "_" + fileName;
}
我們將代碼“filename = filename.substring(index + 1);”修改爲:filename = uuidName(filename.substring(index + 1));