JavaWeb實現文件上傳下載功能實例解析

http://www.jb51.net/article/70543.htm
html

JavaWeb實現文件上傳下載功能實例解析

做者:孤傲蒼狼 字體:[增長 減少] 類型:轉載 時間:2015-08-04 我要評論java

這篇文章主要介紹了JavaWeb中的文件上傳和下載功能的實現,在Web應用系統開發中,文件上傳和下載功能是很是經常使用的功能,須要的朋友能夠參考下
在Web應用系統開發中,文件上傳和下載功能是很是經常使用的功能,今天來說一下JavaWeb中的文件上傳和下載功能的實現。

  對於文件上傳,瀏覽器在上傳的過程當中是將文件以流的形式提交到服務器端的,若是直接使用Servlet獲取上傳文件的輸入流而後再解析裏面的請求參數是比較麻煩,因此通常選擇採用apache的開源工具common-fileupload這個文件上傳組件。這個common-fileupload上傳組件的jar包能夠去apache官網上面下載,也能夠在struts的lib文件夾下面找到,struts上傳的功能就是基於這個實現的。common-fileupload是依賴於common-io這個包的,因此還須要下載這個包。web

1、開發環境搭建算法

  建立一個FileUploadAndDownLoad項目,加入Apache的commons-fileupload文件上傳組件的相關Jar包,以下圖所示:數據庫

  

2、實現文件上傳apache

2.一、文件上傳頁面和消息提示頁面數組

upload.jsp頁面的代碼以下:瀏覽器

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<%@ page language= "java" pageEncoding= "UTF-8" %>
<!DOCTYPE HTML>
<html>
  <head>
   <title>文件上傳</title>
  </head>
  
  <body>
   <form action= "${pageContext.request.contextPath}/servlet/UploadHandleServlet" enctype= "multipart/form-data" method= "post" >
     上傳用戶:<input type= "text" name= "username" ><br/>
     上傳文件1:<input type= "file" name= "file1" ><br/>
     上傳文件2:<input type= "file" name= "file2" ><br/>
     <input type= "submit" value= "提交" >
   </form>
  </body>
</html>

message.jsp的代碼以下:安全

?
1
2
3
4
5
6
7
8
9
10
11
<%@ page language= "java" pageEncoding= "UTF-8" %>
<!DOCTYPE HTML>
<html>
  <head>
   <title>消息提示</title>
  </head>
  
  <body>
     ${message}
  </body>
</html>

2.二、處理文件上傳的Servlet服務器

UploadHandleServlet的代碼以下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
package me.gacl.web.controller;
 
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
 
public class UploadHandleServlet extends HttpServlet {
 
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//獲得上傳文件的保存目錄,將上傳的文件存放於WEB-INF目錄下,不容許外界直接訪問,保證上傳文件的安全
String savePath = this .getServletContext().getRealPath( "/WEB-INF/upload" );
File file = new File(savePath);
//判斷上傳文件的保存目錄是否存在
if (!file.exists() && !file.isDirectory()) {
System.out.println(savePath+ "目錄不存在,須要建立" );
//建立目錄
file.mkdir();
}
//消息提示
String message = "" ;
try {
//使用Apache文件上傳組件處理文件上傳步驟:
//一、建立一個DiskFileItemFactory工廠
DiskFileItemFactory factory = new DiskFileItemFactory();
//二、建立一個文件上傳解析器
ServletFileUpload upload = new ServletFileUpload(factory);
//解決上傳文件名的中文亂碼
upload.setHeaderEncoding( "UTF-8" );
//三、判斷提交上來的數據是不是上傳表單的數據
if (!ServletFileUpload.isMultipartContent(request)){
//按照傳統方式獲取數據
return ;
}
//四、使用ServletFileUpload解析器解析上傳數據,解析結果返回的是一個List<FileItem>集合,每個FileItem對應一個Form表單的輸入項
List<FileItem> list = upload.parseRequest(request);
for (FileItem item : list){
//若是fileitem中封裝的是普通輸入項的數據
if (item.isFormField()){
String name = item.getFieldName();
//解決普通輸入項的數據的中文亂碼問題
String value = item.getString( "UTF-8" );
//value = new String(value.getBytes("iso8859-1"),"UTF-8");
System.out.println(name + "=" + value);
} else { //若是fileitem中封裝的是上傳文件
//獲得上傳的文件名稱,
String filename = item.getName();
System.out.println(filename);
if (filename== null || filename.trim().equals( "" )){
continue ;
}
//注意:不一樣的瀏覽器提交的文件名是不同的,有些瀏覽器提交上來的文件名是帶有路徑的,如: c:\a\b\1.txt,而有些只是單純的文件名,如:1.txt
//處理獲取到的上傳文件的文件名的路徑部分,只保留文件名部分
filename = filename.substring(filename.lastIndexOf( "\\" )+ 1 );
//獲取item中的上傳文件的輸入流
InputStream in = item.getInputStream();
//建立一個文件輸出流
FileOutputStream out = new FileOutputStream(savePath + "\\" + filename);
//建立一個緩衝區
byte buffer[] = new byte [ 1024 ];
//判斷輸入流中的數據是否已經讀完的標識
int len = 0 ;
//循環將輸入流讀入到緩衝區當中,(len=in.read(buffer))>0就表示in裏面還有數據
while ((len=in.read(buffer))> 0 ){
//使用FileOutputStream輸出流將緩衝區的數據寫入到指定的目錄(savePath + "\\" + filename)當中
out.write(buffer, 0 , len);
}
//關閉輸入流
in.close();
//關閉輸出流
out.close();
//刪除處理文件上傳時生成的臨時文件
item.delete();
message = "文件上傳成功!" ;
}
}
} catch (Exception e) {
message= "文件上傳失敗!" ;
e.printStackTrace();
 
}
request.setAttribute( "message" ,message);
request.getRequestDispatcher( "/message.jsp" ).forward(request, response);
}
 
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
 
doGet(request, response);
}
}

在Web.xml文件中註冊UploadHandleServlet

?
1
2
3
4
5
6
7
8
9
< servlet >
< servlet-name >UploadHandleServlet</ servlet-name >
< servlet-class >me.gacl.web.controller.UploadHandleServlet</ servlet-class >
</ servlet >
 
< servlet-mapping >
< servlet-name >UploadHandleServlet</ servlet-name >
< url-pattern >/servlet/UploadHandleServlet</ url-pattern >
</ servlet-mapping >

運行效果以下:

  

文件上傳成功以後,上傳的文件保存在了WEB-INF目錄下的upload目錄,以下圖所示:

  

2.三、文件上傳的細節

  上述的代碼雖然能夠成功將文件上傳到服務器上面的指定目錄當中,可是文件上傳功能有許多須要注意的小細節問題,如下列出的幾點須要特別注意的

  一、爲保證服務器安全,上傳文件應該放在外界沒法直接訪問的目錄下,好比放於WEB-INF目錄下。

  二、爲防止文件覆蓋的現象發生,要爲上傳文件產生一個惟一的文件名。

  三、爲防止一個目錄下面出現太多文件,要使用hash算法打散存儲。

  四、要限制上傳文件的最大值。

  五、要限制上傳文件的類型,在收到上傳文件名時,判斷後綴名是否合法。

  針對上述提出的5點細節問題,咱們來改進一下UploadHandleServlet,改進後的代碼以下:


?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
package me.gacl.web.controller;
   
  import java.io.File;
  import java.io.FileOutputStream;
  import java.io.IOException;
  import java.io.InputStream;
  import java.util.List;
  import java.util.UUID;
 
  import javax.servlet.ServletException;
  import javax.servlet.http.HttpServlet;
  import javax.servlet.http.HttpServletRequest;
  import javax.servlet.http.HttpServletResponse;
  import org.apache.commons.fileupload.FileItem;
  import org.apache.commons.fileupload.FileUploadBase;
  import org.apache.commons.fileupload.ProgressListener;
  import org.apache.commons.fileupload.disk.DiskFileItemFactory;
  import org.apache.commons.fileupload.servlet.ServletFileUpload;
  
  /**
  * @ClassName: UploadHandleServlet
  * @Description: TODO(這裏用一句話描述這個類的做用)
  * @author: 孤傲蒼狼
  * @date: 2015-1-3 下午11:35:50
  *
  */
  public class UploadHandleServlet extends HttpServlet {
  
    public void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
          //獲得上傳文件的保存目錄,將上傳的文件存放於WEB-INF目錄下,不容許外界直接訪問,保證上傳文件的安全
          String savePath = this .getServletContext().getRealPath( "/WEB-INF/upload" );
          //上傳時生成的臨時文件保存目錄
          String tempPath = this .getServletContext().getRealPath( "/WEB-INF/temp" );
          File tmpFile = new File(tempPath);
          if (!tmpFile.exists()) {
            //建立臨時目錄
            tmpFile.mkdir();
          }
          
          //消息提示
          String message = "" ;
          try {
            //使用Apache文件上傳組件處理文件上傳步驟:
            //一、建立一個DiskFileItemFactory工廠
            DiskFileItemFactory factory = new DiskFileItemFactory();
            //設置工廠的緩衝區的大小,當上傳的文件大小超過緩衝區的大小時,就會生成一個臨時文件存放到指定的臨時目錄當中。
            factory.setSizeThreshold( 1024 * 100 ); //設置緩衝區的大小爲100KB,若是不指定,那麼緩衝區的大小默認是10KB
            //設置上傳時生成的臨時文件的保存目錄
            factory.setRepository(tmpFile);
            //二、建立一個文件上傳解析器
            ServletFileUpload upload = new ServletFileUpload(factory);
            //監聽文件上傳進度
            upload.setProgressListener( new ProgressListener(){
              public void update( long pBytesRead, long pContentLength, int arg2) {
                System.out.println( "文件大小爲:" + pContentLength + ",當前已處理:" + pBytesRead);
                /**
                 * 文件大小爲:14608,當前已處理:4096
                  文件大小爲:14608,當前已處理:7367
                  文件大小爲:14608,當前已處理:11419
                  文件大小爲:14608,當前已處理:14608
                 */
              }
            });
             //解決上傳文件名的中文亂碼
            upload.setHeaderEncoding( "UTF-8" );
            //三、判斷提交上來的數據是不是上傳表單的數據
            if (!ServletFileUpload.isMultipartContent(request)){
              //按照傳統方式獲取數據
              return ;
            }
            
            //設置上傳單個文件的大小的最大值,目前是設置爲1024*1024字節,也就是1MB
            upload.setFileSizeMax( 1024 * 1024 );
            //設置上傳文件總量的最大值,最大值=同時上傳的多個文件的大小的最大值的和,目前設置爲10MB
            upload.setSizeMax( 1024 * 1024 * 10 );
            //四、使用ServletFileUpload解析器解析上傳數據,解析結果返回的是一個List<FileItem>集合,每個FileItem對應一個Form表單的輸入項
            List<FileItem> list = upload.parseRequest(request);
            for (FileItem item : list){
              //若是fileitem中封裝的是普通輸入項的數據
              if (item.isFormField()){
                String name = item.getFieldName();
                //解決普通輸入項的數據的中文亂碼問題
                String value = item.getString( "UTF-8" );
                //value = new String(value.getBytes("iso8859-1"),"UTF-8");
                System.out.println(name + "=" + value);
              } else { //若是fileitem中封裝的是上傳文件
                //獲得上傳的文件名稱,
                String filename = item.getName();
                System.out.println(filename);
                if (filename== null || filename.trim().equals( "" )){
                  continue ;
                }
                //注意:不一樣的瀏覽器提交的文件名是不同的,有些瀏覽器提交上來的文件名是帶有路徑的,如: c:\a\b\1.txt,而有些只是單純的文件名,如:1.txt
                //處理獲取到的上傳文件的文件名的路徑部分,只保留文件名部分
                filename = filename.substring(filename.lastIndexOf( "\\" )+ 1 );
                //獲得上傳文件的擴展名
                String fileExtName = filename.substring(filename.lastIndexOf( "." )+ 1 );
                //若是須要限制上傳的文件類型,那麼能夠經過文件的擴展名來判斷上傳的文件類型是否合法
                System.out.println( "上傳的文件的擴展名是:" +fileExtName);
                //獲取item中的上傳文件的輸入流
                InputStream in = item.getInputStream();
                //獲得文件保存的名稱
                String saveFilename = makeFileName(filename);
                //獲得文件的保存目錄
                String realSavePath = makePath(saveFilename, savePath);
                //建立一個文件輸出流
                FileOutputStream out = new FileOutputStream(realSavePath + "\\" + saveFilename);
               //建立一個緩衝區
                byte buffer[] = new byte [ 1024 ];
                //判斷輸入流中的數據是否已經讀完的標識               int len = 0;
                //循環將輸入流讀入到緩衝區當中,(len=in.read(buffer))>0就表示in裏面還有數據
                while ((len=in.read(buffer))> 0 ){
                  //使用FileOutputStream輸出流將緩衝區的數據寫入到指定的目錄(savePath + "\\" + filename)當中
                  out.write(buffer, 0 , len);
                }
                //關閉輸入流
                in.close();
                //關閉輸出流
                out.close();               //刪除處理文件上傳時生成的臨時文件               //item.delete();               message = "文件上傳成功!";
              }
            }
          } catch (FileUploadBase.FileSizeLimitExceededException e) {
            e.printStackTrace();
            request.setAttribute( "message" , "單個文件超出最大值!!!" );
            request.getRequestDispatcher( "/message.jsp" ).forward(request, response);
            return ;
          } catch (FileUploadBase.SizeLimitExceededException e) {
            e.printStackTrace();
            request.setAttribute( "message" , "上傳文件的總的大小超出限制的最大值!!!" );
            request.getRequestDispatcher( "/message.jsp" ).forward(request, response);
            return ;
          } catch (Exception e) {
            message= "文件上傳失敗!" ;
            e.printStackTrace();
          }
          request.setAttribute( "message" ,message);
          request.getRequestDispatcher( "/message.jsp" ).forward(request, response);
    }
    
    /**
    * @Method: makeFileName
    * @Description: 生成上傳文件的文件名,文件名以:uuid+"_"+文件的原始名稱
    * @Anthor:孤傲蒼狼
    * @param filename 文件的原始名稱
    * @return uuid+"_"+文件的原始名稱
    */
    private String makeFileName(String filename){ //2.jpg
      //爲防止文件覆蓋的現象發生,要爲上傳文件產生一個惟一的文件名
      return UUID.randomUUID().toString() + "_" + filename;
    }  
    /**
    * 爲防止一個目錄下面出現太多文件,要使用hash算法打散存儲
    * @Method: makePath
    * @Description:
    * @Anthor:孤傲蒼狼
    *
    * @param filename 文件名,要根據文件名生成存儲目錄
    * @param savePath 文件存儲路徑
    * @return 新的存儲目錄
    */
   private String makePath(String filename,String savePath){
      //獲得文件名的hashCode的值,獲得的就是filename這個字符串對象在內存中的地址
      int hashcode = filename.hashCode();
      int dir1 = hashcode& 0xf ; //0--15
      int dir2 = (hashcode& 0xf0 )>> 4 ; //0-15
      //構造新的保存目錄
      String dir = savePath + "\\" + dir1 + "\\" + dir2; //upload\2\3 upload\3\5
      //File既能夠表明文件也能夠表明目錄     File file = new File(dir);
     //若是目錄不存在
      if (!file.exists()){
        //建立目錄
        file.mkdirs();
     }
      return dir;
    }
  
    public void doPost(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
  
      doGet(request, response);
    }
  }

針對上述提出的5點小細節問題進行改進以後,咱們的文件上傳功能就算是作得比較完善了。

3、文件下載

3.一、列出提供下載的文件資源

  咱們要將Web應用系統中的文件資源提供給用戶進行下載,首先咱們要有一個頁面列出上傳文件目錄下的全部文件,當用戶點擊文件下載超連接時就進行下載操做,編寫一個ListFileServlet,用於列出Web應用系統中全部下載文件。

ListFileServlet的代碼以下:


?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
相關文章
相關標籤/搜索