1、文件上傳概述html
<form action="#" method="post" enctype="multipart/form-data"> <input type="file" name="filename1"/><br> <input type="file" name="filename2"/><br> <input type="submit" value="上傳"/> <form> <!-- 一、表單方式必須是post 二、必須設置encType屬性爲 multipart/form-data.設置該值後,瀏覽器在上傳文件時,將會把文件數據附帶在http請求消息體中,
並使用MIME協議對上傳的文件進行描述,以方便接收方對上傳數據進行解析和處理。 三、必需要設置input的name屬性,不然瀏覽器將不會發送上傳文件的數據。 -->
二、在Servlet中讀取文件上傳數據,並保存到服務器硬盤java
Accept-Language: zh-Hans-CN,zh-Hans;q=0.5
Content-Type: multipart/form-data; boundary=---------------------------7dfa01d1908a4
UA-CPU: AMD64
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/5.0 (Windows NT 6.2; Win64; x64; Trident/7.0; rv:11.0) like Gecko
Content-Length: 653
Host: localhost:8080
Connection: Keep-Alive
Pragma: no-cache
Cookie: JSESSIONID=11CEFF8E271AB62CE676B5A87B746B5F
-----------------------------7dfa01d1908a4
Content-Disposition: form-data; name="username"
zhangsan
-----------------------------7dfa01d1908a4
Content-Disposition: form-data; name="userpass"
1234
-----------------------------7dfa01d1908a4
Content-Disposition: form-data; name="filename1"; filename="C:\Users\ASUS\Desktop\upload.txt"
Content-Type: text/plain
this is first file content!
-----------------------------7dfa01d1908a4
Content-Disposition: form-data; name="filename1"; filename="C:\Users\ASUS\Desktop\upload2.txt"
Content-Type: text/plain
this is Second file content!
hello
-----------------------------7dfa01d1908a4--
從上面的數據中也能夠看出,若是本身手工的去分割讀取數據很難寫出健壯穩定的程序。因此,爲方便用戶處理上傳數據,Apache開源組織提供了一個用來處理表單文件上傳的一個開源組件(Commons-fileupload),該組件性能優異,而且其API使用極其簡單,可讓開發人員輕鬆實現web文件上傳功能,所以在web開發中實現文件上傳功能,一般使用Commons-fileupload組件實現。web
1 response.setContentType("text/html;charset=utf-8");//設置響應編碼 2 request.setCharacterEncoding("utf-8"); 3 PrintWriter writer = response.getWriter();//獲取響應輸出流 4 5 ServletInputStream inputStream = request.getInputStream();//獲取請求輸入流 6 7 /* 8 * 一、建立DiskFileItemFactory對象,設置緩衝區大小和臨時文件目錄 9 * 該類有兩個構造方法一個是無參的構造方法, 10 * 另外一個是帶兩個參數的構造方法 11 * @param int sizeThreshold,該參數設置內存緩衝區的大小,默認值爲10K。當上傳文件大於緩衝區大小時,fileupload組件將使用臨時文件緩存上傳文件 12 * @param java.io.File repository,該參數指定臨時文件目錄,默認值爲System.getProperty("java.io.tmpdir"); 13 * 14 * 若是使用了無參的構造方法,則使用setSizeThreshold(int sizeThreshold),setRepository(java.io.File repository) 15 * 方法手動進行設置 16 */ 17 DiskFileItemFactory factory = new DiskFileItemFactory(); 18 19 int sizeThreshold=1024*1024; 20 factory.setSizeThreshold(sizeThreshold); 21 22 File repository = new File(request.getSession().getServletContext().getRealPath("temp")); 23 // System.out.println(request.getSession().getServletContext().getRealPath("temp")); 24 // System.out.println(request.getRealPath("temp")); 25 factory.setRepository(repository); 26 27 /* 28 * 二、使用DiskFileItemFactory對象建立ServletFileUpload對象,並設置上傳文件的大小 29 * 30 * ServletFileUpload對象負責處理上傳的文件數據,並將表單中每一個輸入項封裝成一個FileItem 31 * 該對象的經常使用方法有: 32 * boolean isMultipartContent(request);判斷上傳表單是否爲multipart/form-data類型 33 * List parseRequest(request);解析request對象,並把表單中的每個輸入項包裝成一個fileItem 對象,並返回一個保存了全部FileItem的list集合 34 * void setFileSizeMax(long filesizeMax);設置單個上傳文件的最大值 35 * void setSizeMax(long sizeMax);設置上傳溫江總量的最大值 36 * void setHeaderEncoding();設置編碼格式,解決上傳文件名亂碼問題 37 */ 38 ServletFileUpload upload = new ServletFileUpload(factory); 39 40 upload.setHeaderEncoding("utf-8");//設置編碼格式,解決上傳文件名亂碼問題 41 /* 42 * 三、調用ServletFileUpload.parseRequest方法解析request對象,獲得一個保存了全部上傳內容的List對象 43 */ 44 List<FileItem> parseRequest=null; 45 try { 46 parseRequest = upload.parseRequest(request); 47 } catch (FileUploadException e) { 48 e.printStackTrace(); 49 } 50 /* 51 * 四、對list進行迭代,每迭代一個FileItem對象,調用其isFormField方法判斷是不是文件上傳 52 * true表示是普通表單字段,則調用getFieldName、getString方法獲得字段名和字段值 53 * false爲上傳文件,則調用getInputStream方法獲得數據輸入流,從而讀取上傳數據 54 * 55 * FileItem用來表示文件上傳表單中的一個上傳文件對象或者普通的表單對象 56 * 該對象經常使用方法有: 57 * boolean isFormField();判斷FileItem是一個文件上傳對象仍是普通表單對象 58 * true表示是普通表單字段, 59 * 則調用getFieldName、getString方法獲得字段名和字段值 60 * false爲上傳文件, 61 * 則調用getName()得到上傳文件的文件名,注意:有些瀏覽器會攜帶客戶端路徑,須要本身減除 62 * 調用getInputStream()方法獲得數據輸入流,從而讀取上傳數據 63 * delete(); 表示在關閉FileItem輸入流後,刪除臨時文件。 64 */ 65 66 for (FileItem fileItem : parseRequest) { 67 if (fileItem.isFormField()) {//表示普通字段 68 if ("username".equals(fileItem.getFieldName())) { 69 String username = fileItem.getString(); 70 writer.write("您的用戶名:"+username+"<br>"); 71 } 72 if ("userpass".equals(fileItem.getFieldName())) { 73 String userpass = fileItem.getString(); 74 writer.write("您的密碼:"+userpass+"<br>"); 75 } 76 77 }else {//表示是上傳的文件 78 //不一樣瀏覽器上傳的文件可能帶有路徑名,須要本身切割 79 String clientName = fileItem.getName(); 80 String filename = ""; 81 if (clientName.contains("\\")) {//若是包含"\"表示是一個帶路徑的名字,則截取最後的文件名 82 filename = clientName.substring(clientName.lastIndexOf("\\")).substring(1); 83 }else { 84 filename = clientName; 85 } 86 87 UUID randomUUID = UUID.randomUUID();//生成一個128位長的全球惟一標識 88 89 filename = randomUUID.toString()+filename; 90 91 /* 92 * 設計一個目錄生成算法,若是所用用戶上傳的文件總數是億數量級的或更多,放在同一個目錄下回致使文件索引很是慢, 93 * 因此,設計一個目錄結構來分散存放文件是很是有必要,且合理的 94 * 將UUID取哈希算法,散列到更小的範圍, 95 * 將UUID的hashcode轉換爲一個8位的8進制字符串, 96 * 從這個字符串的第一位開始,每個字符表明一級目錄,這樣就構建了一個八級目錄,每一級目錄中最多有16個子目錄 97 * 這不管對於服務器仍是操做系統都是很是高效的目錄結構 98 */ 99 int hashUUID =randomUUID.hashCode(); 100 String hexUUID = Integer.toHexString(hashUUID); 101 //System.out.println(hexUUID); 102 //獲取將上傳的文件存存儲在哪一個文件夾下的絕對路徑 103 String filepath=request.getSession().getServletContext().getRealPath("upload"); 104 for (char c : hexUUID.toCharArray()) { 105 filepath = filepath+"/"+c; 106 } 107 //若是目錄不存在就生成八級目錄 108 File filepathFile = new File(filepath); 109 if (!filepathFile.exists()) { 110 filepathFile.mkdirs(); 111 } 112 //從Request輸入流中讀取文件,並寫入到服務器 113 InputStream inputStream2 = fileItem.getInputStream(); 114 //在服務器端建立文件 115 File file = new File(filepath+"/"+filename); 116 BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file)); 117 118 byte[] buffer = new byte[10*1024]; 119 int len = 0; 120 while ((len= inputStream2.read(buffer, 0, 10*1024))!=-1) { 121 bos.write(buffer, 0, len); 122 } 123 writer.write("您上傳文件"+clientName+"成功<br>"); 124 //關閉資源 125 bos.close(); 126 inputStream2.close(); 127 } 128 } 129 //注意Eclipse的上傳的文件是保存在項目的運行目錄,而不是workspace中的工程目錄裏。
2、文件上傳須要特別注意的問題:(這些問題在上面的代碼中都提供了簡單的解決)算法
java.util.UUID,直接調用便可. UUID uuid = UUID.randomUUID(); String s = UUID.randomUUID().toString();//用來生成數據庫的主鍵id很是不錯。。 UUID是由一個十六位的數字組成,表現出來的形式例如 550E8400-E29B-11D4-A716-446655440000
三、爲防止單個目錄下文件過多,影響文件讀寫速度,處理上傳文件的程序應該應根據可能的上傳總量,選擇合適的目錄結構生成算法,將上傳文件分散存儲。如使用hashcode方法構建多級目錄。數據庫
<% ArrayList<String> fileNames = new ArrayList<String>(); fileNames.add("file/aa.txt"); fileNames.add("file/bb.jpg"); for(String fileName : fileNames) { %> <form action="DownloadServlet" method="get"> <input type="hidden" name="fileName" value="<%=fileName %>" /> <input type="submit" value="下載:<%=fileName %>" /> </form> <% } %>
request.setCharacterEncoding("utf-8"); String filename = request.getParameter("fileName"); String urlname = URLEncoder.encode(filename, "utf-8");//防止文件名中有中文亂碼 response.setHeader("Content-Disposition","attachment;filename="+urlname); FileInputStream fis = new FileInputStream(new File(request.getSession().getServletContext().getRealPath(filename))); BufferedInputStream bis = new BufferedInputStream(fis); ServletOutputStream sos = response.getOutputStream(); byte[] buffer = new byte[1024]; int len=0; while((len=bis.read(buffer, 0, 1024))!=-1){ sos.write(buffer, 0, len); } bis.close(); fis.close();