文件上傳 - Commons FileUpload介紹


一、概述

FileUpload能夠以多種不同的方式使用,具體取決於應用程序的要求。在最簡單的情況下,調用單個方法來解析servlet請求,然后處理解析出來的Item集合。此外也可以自定義FileUpload已完全控制各個Item的存儲方式,比如設置緩存目錄、直接將接收到的Item以流的形式寫入數據庫等。

FileUpload依賴於Commons IO,因此類路徑下要有Commons IO的jar包。當然采用Maven依賴的方式不用擔心,maven會自動下載Commons IO包。

二、FIleUpload使用

上傳文件,注意幾個問題

① form表單內,要添加空間<input type="file" name="myfile">
② form表單的內容格式要定義成multipart/form-data格式:<form action="admin/good_add" method="post" enctype="multipart/form-data">
③ 需要類庫:1 commons-io.jar ,2 commons-fileupload-1.3.1.jar

接下來我們看下他的用法。

1)創建工廠類

DiskFileItemFactory factory = new DiskFileItemFactory();

FileItemFactory可以設置內容:

 ● setRepository(File dir);//設置臨時文件存儲位置。

 ● setSizeThreshold(long bytes);//設置請求大小閾值,當請求大於該值時,接收到的數據是緩存在磁盤中的,否則直接緩存到內存中。

 ● setFileCleaningTracker(FileCleaningTracker pTracker);//設置臨時文件清理跟蹤器

2)創建核心類-文件解析對象

ServletFileUpload upload = new ServletFileUpload(DiskFileItemFactory factory);

ServletFileUpload可以設置內容:

 ● setSizeMax(long bytes);//設置整個請求的最大值,大於該值時,不允許傳送。

 ● setFileMaxSize(long bytes);//設置單個文件的最大值,大於該值時,不允許傳送。

 ● setHeaderEncoding(String charset);//設置讀取每個FileItem的頭數據的字符編碼,不設置時采用request編碼,也沒有時采用系統默認編碼。

 ● serProgressListener(ProgressListener pListener);//設置上傳進度監聽器

3)解析request請求-進行文件解析后放在List中,因為這個類庫支持多個文件上傳,因此把結果會存在List中。

List<FileItem> list = upload.parseRequest(request);

FileItem能夠獲取的內容:

 ● getContentType();//獲取單個Item的ContentType

 ● getName();//獲取Item本來的文件名,如果不是文件則為null

 ● getFieldName();//獲取Item的field名

 ● getSize();//獲取Item的大小

 ● get();//將Item轉換成字節數組返回

 ● isInMemory();//Item目前是否存在內存中

 ● isFormField();//是否是表單域

 ● getInputStream();//獲取輸入流,用於讀取Item

二、上傳原理和代碼分析

上傳:將需要上傳的資源,發送給服務器,在服務器中保存下來。

下載:下載資源時,將服務器上的某個資源發送給瀏覽器。

 

1)瀏覽器端

注意:enctype=multipart/form-data:該屬性表明發送的請求體的內容是多表單元素的,通俗點講,就是有各種各樣的數據,可能有二進制數據,也可能有表單數據,等等。

 1 <form class="form-horizontal" action="${pageContext.request.contextPath }/admin/good_add" method="post" enctype="multipart/form-data">
 2     <div class="form-group">
 3         <label for="input_name" class="col-sm-1 control-label">名稱</label>
 4         <div class="col-sm-6">
 5             <input type="text" class="form-control" id="input_name" name="name" required="required">
 6         </div>
 7     </div>
 8     <div class="form-group">
 9         <label for="input_name" class="col-sm-1 control-label">價格</label>
10         <div class="col-sm-6">
11             <input type="text" class="form-control" id="input_name" name="price">
12         </div>
13     </div>
14     <div class="form-group">
15         <label for="input_name" class="col-sm-1 control-label">介紹</label>
16         <div class="col-sm-6">
17             <input type="text" class="form-control" id="input_name" name="intro">
18         </div>
19     </div>
20     <div class="form-group">
21         <label for="input_name" class="col-sm-1 control-label">庫存</label>
22         <div class="col-sm-6">
23             <input type="text" class="form-control" id="input_name" name="stock">
24         </div>
25     </div>
26     <div class="form-group">
27         <label for="input_file" class="col-sm-1 control-label">封面圖片</label>
28         <div class="col-sm-6">
29             <input type="file" name="cover" id="input_file" required="required">推薦尺寸: 500 * 500
30         </div>
31     </div>
32     <div class="form-group">
33         <label for="input_file" class="col-sm-1 control-label">詳情圖片1</label>
34         <div class="col-sm-6">
35             <input type="file" name="image1" id="input_file" required="required">推薦尺寸: 500 * 500
36         </div>
37     </div>
38     <div class="form-group">
39         <label for="input_file" class="col-sm-1 control-label">詳情圖片2</label>
40         <div class="col-sm-6">
41             <input type="file" name="image2" id="input_file" required="required">推薦尺寸: 500 * 500
42         </div>
43     </div>
44     <div class="form-group">
45         <label for="select_topic" class="col-sm-1 control-label">類目</label>
46         <div class="col-sm-6">
47             <select class="form-control" id="select_topic" name="good.type.id">
48                 <c:forEach items="${typeList }" var="t">
49                     <option value="${t.id }">${t.name }</option>
50                 </c:forEach>
51             </select>
52         </div>
53     </div>
54     <div class="form-group">
55         <div class="col-sm-offset-1 col-sm-10">
56             <button type="submit" class="btn btn-success">提交保存</button>
57         </div>
58     </div>
59 </form>
My Code

使用multipart/form-data會有一個boundary屬性,來用將提交的表單數據進行分隔,以用來讓服務器知道哪個是我們上傳的資源,哪個是普通的表單數據。

1 <form action="adctionServlet" method="post" enctype="enctype=multipart/form-data">
2     描述:<input type="text" name=""/><br/>
3     圖片:<input type="file" name="image"/><br/>
4     <input type="submit" value="上傳"/>
5 </form>

2)服務器端

使用commons-fileupload進行處理上傳內容。

 1 try {
 2     DiskFileItemFactory factory = new DiskFileItemFactory();
 3     ServletFileUpload upload = new ServletFileUpload(factory);
 4 
 5     List<FileItem> list = upload.parseRequest(request);
 6     Goods goods = new Goods();
 7     for (FileItem item : list) {
 8 
 9         // 判斷itme;true-為普通輸入項,false-為文件
10         if (item.isFormField()) {
11 
12             // 設置商品屬性
13             switch (item.getFieldName()) {
14             case "name":
15                 goods.setName(item.getString("utf-8"));
16                 break;
17             case "price":
18                 goods.setPrice(Float.parseFloat(item.getString("utf-8")));
19                 break;
20             case "intro":
21                 goods.setIntro(item.getString("utf-8"));
22                 break;
23             case "stock":
24                 goods.setStock(Integer.parseInt(item.getString("utf-8")));
25                 break;
26             case "good.type.id":
27                 goods.setTypeid(Integer.parseInt(item.getString("utf-8")));
28                 break;
29             }
30         } else {
31 
32             if (item.getInputStream().available() <= 0) {
33                 continue;
34             }
35             // 1-獲取文件名;2-獲取后綴名;3-根據毫秒值設置新的文件名;4-創建保存路徑
36             String fileName = item.getName();
37             fileName = fileName.substring(fileName.indexOf("."));
38             fileName = "/" + System.currentTimeMillis() + fileName;
39             String path = this.getServletContext().getRealPath("/picture") + fileName;
40 
41             // 讀取上傳
42             InputStream in = item.getInputStream();
43             FileOutputStream out = new FileOutputStream(path);
44             byte[] buff = new byte[1024];
45             int len = 0;
46             while ((len = in.read(buff)) > 0) {
47                 out.write(buff, 0, len);
48             }
49             in.close();
50             out.close();
51 
52             // 設置商品屬性
53             switch (item.getFieldName()) {
54             case "cover":
55                 goods.setCover("/picture" + fileName);
56                 break;
57             case "image1":
58                 goods.setImage1("/picture" + fileName);
59                 break;
60             case "image2":
61                 goods.setImage2("/picture" + fileName);
62                 break;
63             }
64         }
65         item.delete();
66     }
67 } catch (FileUploadException e) {
68     e.printStackTrace();
69 }    
My Code

上傳的各種問題:

1,上傳文件名亂碼問題:使用servletFileUpload.setHeaderEncoding("UTF-8");或者request.setCharacterEncoding("UTF-8")都可以。

2,表單內容亂碼問題:使用getString("utf-8")即可,也就是在獲取內容時,就可以設置碼表。

3,上傳文件同名問題:使用UUID.randomUUID().toString().replace("-", "").獲得一個獨一無二的32位數字。

4,使用FileUtils.copyInputStreamToFile(is, file);來將內容輸出到指定路徑文件中去,mkdirs() 自動創建目錄

 1 public void doGet(HttpServletRequest request, HttpServletResponse response)
 2         throws ServletException, IOException {
 3     try {
 4         
 5         //0.5 檢查是否支持文件上傳  ,檢查請求頭Content-Type : multipart/form-data 
 6         if(!ServletFileUpload.isMultipartContent(request)){
 7             throw new RuntimeException("不要得瑟,沒用");
 8         }
 9         
10         //1 工廠
11         DiskFileItemFactory fileItemFactory = new DiskFileItemFactory();
12         // 1.1 設置是否生產臨時文件臨界值。大於2M生產臨時文件。保證:上傳數據完整性。
13         fileItemFactory.setSizeThreshold(1024 * 1024 * 2);  //2MB
14         // 1.2 設置臨時文件存放位置
15         // * 臨時文件擴展名  *.tmp  ,臨時文件可以任意刪除。
16         String tempDir = this.getServletContext().getRealPath("/temp");
17         fileItemFactory.setRepository(new File(tempDir));
18         
19         //2 核心類
20         ServletFileUpload servletFileUpload = new ServletFileUpload(fileItemFactory);
21         // 2.1 如果使用無參構造  ServletFileUpload() ,手動設置工廠
22         //servletFileUpload.setFileItemFactory(fileItemFactory);
23         // 2.2 單個上傳文件大小
24         //servletFileUpload.setFileSizeMax(1024*1024 * 2);  //2M
25         // 2.3 整個上傳文件總大小
26         //servletFileUpload.setSizeMax(1024*1024*10);        //10M
27         // 2.4 設置上傳文件名的亂碼
28         // * 首先使用  setHeaderEncoding 設置編碼
29         // * 如果沒有設置將使用請求編碼 request.setCharacterEncoding("UTF-8")
30         // * 以上都沒有設置,將使用平台默認編碼
31         servletFileUpload.setHeaderEncoding("UTF-8");
32         // 2.5 上傳文件進度,提供監聽器進行監聽。
33         servletFileUpload.setProgressListener(new MyProgressListener());
34         
35         
36         //3 解析request  ,List存放 FileItem (表單元素的封裝對象,一個<input>對應一個對象)
37         List<FileItem> list = servletFileUpload.parseRequest(request);
38         
39         //4 遍歷集合獲得數據
40         for (FileItem fileItem : list) {
41             // 判斷
42             if(fileItem.isFormField()){
43                 // 5 是否為表單字段(普通表單元素)
44                 //5.1.表單字段名稱
45                 String fieldName = fileItem.getFieldName();
46                 System.out.println(fieldName);
47                 //5.2.表單字段值 , 解決普通表單內容的亂碼
48                 String fieldValue = fileItem.getString("UTF-8");
49                 System.out.println(fieldValue);
50             } else {
51                 //6 上傳字段(上傳表單元素)
52                 //6.1.表單字段名稱  fileItem.getFieldName();
53                 //6.2.上傳文件名
54                 String fileName = fileItem.getName();
55                 // * 兼容瀏覽器, IE : C:\Users\liangtong\Desktop\abc.txt  ; 其他瀏覽器 : abc.txt
56                 fileName = fileName.substring(fileName.lastIndexOf("\\") + 1);
57                 // * 文件重名
58                 fileName = UUID.randomUUID().toString().replace("-", "") + fileName;
59                 // * 單個文件夾文件個數過多?
60                 String subDir = StringUtils.getDir(fileName);
61                 
62                 System.out.println(fileName);
63                 //6.3.上傳內容
64                 InputStream is = fileItem.getInputStream();
65                 String parentDir = this.getServletContext().getRealPath("/WEB-INF/upload");
66                 File file = new File(parentDir + subDir,fileName);
67 
68                 // 將指定流 寫入 到 指定文件中  -- mkdirs() 自動創建目錄
69                 FileUtils.copyInputStreamToFile(is, file);
70                 
71                 //7刪除臨時文件
72                 fileItem.delete();
73             }
74         }
75         
76     } catch (Exception e) {
77         e.printStackTrace();
78         
79         throw new RuntimeException(e);
80         
81     }
82 }

總結上傳

其實理解了也不是很難,就是上傳文件后的處理比較麻煩,各種小問題,存儲過程最為麻煩。

1、創建工廠類

2、使用核心類,

3、解析request請求,

4、遍歷請求體的內容,將上傳內容和普通表單內容都獲取出來

5、獲取到上傳內容時,對其存儲位置進行設置

 

@部分轉載自:https://www.cnblogs.com/whgk/p/6479405.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM