文件上傳在web應(yīng)用中非常普遍,要在jsp環(huán)境中實(shí)現(xiàn)文件上傳功能是非常容易的,因?yàn)榫W(wǎng)上有許多用java開發(fā)的文件上傳組件,本文以commons-fileupload組件為例,為jsp應(yīng)用添加文件上傳功能。
common-fileupload組件是apache的一個(gè)開源項(xiàng)目之一,可以從http://jakarta.apache.org/commons/fileupload/下載。
用該組件可實(shí)現(xiàn)一次上傳一個(gè)或多個(gè)文件,并可限制文件大小。
下載后解壓zip包,將commons-fileupload-1.0.jar復(fù)制到tomcat的webapps你的webappWEB-INFlib下,目錄不存在請自建目錄。
新建一個(gè)servlet: Upload.java用于文件上傳:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
import java.io.*; import java.util.*; import javax.servlet.*; import javax.servlet.http.*; import org.apache.commons.fileupload.*; public class Upload extends HttpServlet { private String uploadPath = "C:upload" ; // 上傳文件的目錄 private String tempPath = "C:uploadtmp" ; // 臨時(shí)文件目錄 public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { } } |
在doPost()方法中,當(dāng)servlet收到瀏覽器發(fā)出的Post請求后,實(shí)現(xiàn)文件上傳。以下是示例代碼:
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
|
public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { try { DiskFileUpload fu = new DiskFileUpload(); // 設(shè)置最大文件尺寸,這里是4MB fu.setSizeMax( 4194304 ); // 設(shè)置緩沖區(qū)大小,這里是4kb fu.setSizeThreshold( 4096 ); // 設(shè)置臨時(shí)目錄: fu.setRepositoryPath(tempPath); // 得到所有的文件: List fileItems = fu.parseRequest(request); Iterator i = fileItems.iterator(); // 依次處理每一個(gè)文件: while (i.hasNext()) { FileItem fi = (FileItem)i.next(); // 獲得文件名,這個(gè)文件名包括路徑: String fileName = fi.getName(); // 在這里可以記錄用戶和文件信息 // ... // 寫入文件,暫定文件名為a.txt,可以從fileName中提取文件名: fi.write( new File(uploadPath + "a.txt" )); } } catch (Exception e) { // 可以跳轉(zhuǎn)出錯(cuò)頁面 } } |
如果要在配置文件中讀取指定的上傳文件夾,可以在init()方法中執(zhí)行:
1
2
3
4
5
6
7
8
9
|
public void init() throws ServletException { uploadPath = .... tempPath = .... // 文件夾不存在就自動(dòng)創(chuàng)建: if (! new File(uploadPath).isDirectory()) new File(uploadPath).mkdirs(); if (! new File(tempPath).isDirectory()) new File(tempPath).mkdirs(); } |
編譯該servlet,注意要指定classpath,確保包含commons-upload-1.0.jar和tomcatcommonlibservlet-api.jar。
配置servlet,用記事本打開tomcatwebapps你的webappWEB-INFweb.xml,沒有的話新建一個(gè)。
典型配置如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
〈?xml version= "1.0" encoding= "ISO-8859-1" ?〉 〈!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" 〉 〈web-app〉 〈servlet〉 〈servlet-name〉Upload〈/servlet-name〉 〈servlet- class 〉Upload〈/servlet- class 〉 〈/servlet〉 〈servlet-mapping〉 〈servlet-name〉Upload〈/servlet-name〉 〈url-pattern〉/fileupload〈/url-pattern〉 〈/servlet-mapping〉 〈/web-app〉 |
配置好servlet后,啟動(dòng)tomcat,寫一個(gè)簡單的html測試:
1
2
3
4
5
|
〈form action= "fileupload" method= "post" enctype= "multipart/form-data" name= "form1" 〉 〈input type= "file" name= "file" 〉 〈input type= "submit" name= "Submit" value= "upload" 〉 〈/form〉 |
注意action="fileupload"其中fileupload是配置servlet時(shí)指定的url-pattern。
下面是某個(gè)大蝦的代碼:
這個(gè)Upload比smartUpload好用多了.完全是我一個(gè)個(gè)byte調(diào)試出來的,不象smartUpload的bug具多.
調(diào)用方法:
1
2
3
4
5
6
7
8
|
Upload up = new Upload(); up.init(request); /** 此處可以調(diào)用setSaveDir(String saveDir); 設(shè)置保存路徑 調(diào)用setMaxFileSize(long size)設(shè)置上傳文件的最大字節(jié). 調(diào)用setTagFileName(String)設(shè)置上傳后文件的名字(只對第一個(gè)文件有效) */ up. uploadFile(); |
然后String[] names = up.getFileName(); 得到上傳的文件名,文件絕對路徑應(yīng)該是
保存的目錄saveDir+"/"+names[i];
可以通過up.getParameter("field"); 得到上傳的文本或up.getParameterValues("filed")
得到同名字段如多個(gè)checkBox的值.
其它的自己試試.
源碼如下所示:____________________________________________________________
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
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
|
package com.inmsg.beans; import java.io.*; import java.util.*; import javax.servlet.*; import javax.servlet.http.*; public class Upload { private String saveDir = "." ; //要保存文件的路徑 private String contentType = "" ; //文檔類型 private String charset = "" ; //字符集 private ArrayList tmpFileName = new ArrayList(); //臨時(shí)存放文件名的數(shù)據(jù)結(jié)構(gòu) private Hashtable parameter = new Hashtable(); //存放參數(shù)名和值的數(shù)據(jù)結(jié)構(gòu) private ServletContext context; //程序上下文,用于初始化 private HttpServletRequest request; //用于傳入請求對象的實(shí)例 private String boundary = "" ; //內(nèi)存數(shù)據(jù)的分隔符 private int len = 0 ; //每次從內(nèi)在中實(shí)際讀到的字節(jié)長度 private String queryString; private int count; //上載的文件總數(shù) private String[] fileName; //上載的文件名數(shù)組 private long maxFileSize = 1024 * 1024 * 10 ; //最大文件上載字節(jié); private String tagFileName = "" ; public final void init(HttpServletRequest request) throws ServletException { this .request = request; boundary = request.getContentType().substring( 30 ); //得到內(nèi)存中數(shù)據(jù)分界符 queryString = request.getQueryString(); } public String getParameter(String s) { //用于得到指定字段的參數(shù)值,重寫request.getParameter(String s) if (parameter.isEmpty()) { return null ; } return (String) parameter.get(s); } public String[] getParameterValues(String s) { //用于得到指定同名字段的參數(shù)數(shù)組,重寫request.getParameterValues(String s) ArrayList al = new ArrayList(); if (parameter.isEmpty()) { return null ; } Enumeration e = parameter.keys(); while (e.hasMoreElements()) { String key = (String) e.nextElement(); if ( - 1 != key.indexOf(s + "||||||||||" ) || key.equals(s)) { al.add(parameter.get(key)); } } if (al.size() == 0 ) { return null ; } String[] value = new String[al.size()]; for ( int i = 0 ; i 〈 value.length; i++) { value[i] = (String) al.get(i); } return value; } public String getQueryString() { return queryString; } public int getCount() { return count; } public String[] getFileName() { return fileName; } public void setMaxFileSize( long size) { maxFileSize = size; } public void setTagFileName(String filename) { tagFileName = filename; } public void setSaveDir(String saveDir) { //設(shè)置上載文件要保存的路徑 this .saveDir = saveDir; File testdir = new File(saveDir); //為了保證目錄存在,如果沒有則新建該目錄 if (!testdir.exists()) { testdir.mkdirs(); } } public void setCharset(String charset) { //設(shè)置字符集 this .charset = charset; } public boolean uploadFile() throws ServletException, IOException { //用戶調(diào)用的上載方法 setCharset(request.getCharacterEncoding()); return uploadFile(request.getInputStream()); } private boolean uploadFile(ServletInputStream servletinputstream) throws //取得央存數(shù)據(jù)的主方法 ServletException, IOException { String line = null ; byte [] buffer = new byte [ 256 ]; while ( (line = readLine(buffer, servletinputstream, charset)) != null ) { if (line.startsWith( "Content-Disposition: form-data; " )) { int i = line.indexOf( "filename=" ); if (i 〉= 0 ) { //如果一段分界符內(nèi)的描述中有filename=,說明是文件的編碼內(nèi)容 String fName = getFileName(line); if (fName.equals( "" )) { continue ; } if (count == 0 && tagFileName.length() != 0 ) { String ext = fName.substring( (fName.lastIndexOf( "." ) + 1 )); fName = tagFileName + "." + ext; } tmpFileName.add(fName); count++; while ( (line = readLine(buffer, servletinputstream, charset)) != null ) { if (line.length() 〈= 2 ) { break ; } } File f = new File(saveDir, fName); FileOutputStream dos = new FileOutputStream(f); long size = 0l; while ( (line = readLine(buffer, servletinputstream, null )) != null ) { if (line.indexOf(boundary) != - 1 ) { break ; } size += len; if (size 〉 maxFileSize) { throw new IOException( "文件超過" + maxFileSize + "字節(jié)!" ); } dos.write(buffer, 0 , len); } dos.close(); } else { //否則是字段編碼的內(nèi)容 String key = getKey(line); String value = "" ; while ( (line = readLine(buffer, servletinputstream, charset)) != null ) { if (line.length() 〈= 2 ) { break ; } } while ( (line = readLine(buffer, servletinputstream, charset)) != null ) { if (line.indexOf(boundary) != - 1 ) { break ; } value += line; } put(key, value.trim(), parameter); } } } if (queryString != null ) { String[] each = split(queryString, "&" ); for ( int k = 0 ; k 〈 each.length; k++) { String[] nv = split(each[k], "=" ); if (nv.length == 2 ) { put(nv[ 0 ], nv[ 1 ], parameter); } } } fileName = new String[tmpFileName.size()]; for ( int k = 0 ; k 〈 fileName.length; k++) { fileName[k] = (String) tmpFileName.get(k); //把ArrayList中臨時(shí)文件名倒入數(shù)據(jù)中供用戶調(diào)用 } if (fileName.length == 0 ) { return false ; //如果fileName數(shù)據(jù)為空說明沒有上載任何文件 } return true ; } private void put(String key, String value, Hashtable ht) { if (!ht.containsKey(key)) { ht.put(key, value); } else { //如果已經(jīng)有了同名的KEY,就要把當(dāng)前的key更名,同時(shí)要注意不能構(gòu)成和KEY同名 try { Thread.currentThread().sleep( 1 ); //為了不在同一ms中產(chǎn)生兩個(gè)相同的key } catch (Exception e) {} key += "||||||||||" + System.currentTimeMillis(); ht.put(key, value); } } /* 調(diào)用ServletInputstream.readLine(byte[] b,int offset,length)方法,該方法是從ServletInputstream流中讀一行 到指定的byte數(shù)組,為了保證能夠容納一行,該byte[]b不應(yīng)該小于256,重寫的readLine中,調(diào)用了一個(gè)成員變量len為 實(shí)際讀到的字節(jié)數(shù)(有的行不滿256),則在文件內(nèi)容寫入時(shí)應(yīng)該從byte數(shù)組中寫入這個(gè)len長度的字節(jié)而不是整個(gè)byte[] 的長度,但重寫的這個(gè)方法返回的是String以便分析實(shí)際內(nèi)容,不能返回len,所以把len設(shè)為成員變量,在每次讀操作時(shí) 把實(shí)際長度賦給它. 也就是說在處理到文件的內(nèi)容時(shí)數(shù)據(jù)既要以String形式返回以便分析開始和結(jié)束標(biāo)記,又要同時(shí)以byte[]的形式寫到文件 輸出流中. */ private String readLine( byte [] Linebyte, ServletInputStream servletinputstream, String charset) { try { len = servletinputstream.readLine(Linebyte, 0 , Linebyte.length); if (len == - 1 ) { return null ; } if (charset == null ) { return new String(Linebyte, 0 , len); } else { return new String(Linebyte, 0 , len, charset); } } catch (Exception _ex) { return null ; } } private String getFileName(String line) { //從描述字符串中分離出文件名 if (line == null ) { return "" ; } int i = line.indexOf( "filename=" ); line = line.substring(i + 9 ).trim(); i = line.lastIndexOf( "" ); if (i 〈 0 || i 〉= line.length() - 1 ) { i = line.lastIndexOf( "/" ); if (line.equals( "" "" )) { return "" ; } if (i 〈 0 || i 〉= line.length() - 1 ) { return line; } } return line.substring(i + 1 , line.length() - 1 ); } private String getKey(String line) { //從描述字符串中分離出字段名 if (line == null ) { return "" ; } int i = line.indexOf( "name=" ); line = line.substring(i + 5 ).trim(); return line.substring( 1 , line.length() - 1 ); } public static String[] split(String strOb, String mark) { if (strOb == null ) { return null ; } StringTokenizer st = new StringTokenizer(strOb, mark); ArrayList tmp = new ArrayList(); while (st.hasMoreTokens()) { tmp.add(st.nextToken()); } String[] strArr = new String[tmp.size()]; for ( int i = 0 ; i 〈 tmp.size(); i++) { strArr[i] = (String) tmp.get(i); } return strArr; } } 下載其實(shí)非常簡單,只要如下處理,就不會(huì)發(fā)生問題。 public void downLoad(String filePath,HttpServletResponse response, boolean isOnLine) throws Exception{ File f = new File(filePath); if (!f.exists()){ response.sendError( 404 , "File not found!" ); return ; } BufferedInputStream br = new BufferedInputStream( new FileInputStream(f)); byte [] buf = new byte [ 1024 ]; int len = 0 ; response.reset(); //非常重要 if (isOnLine){ //在線打開方式 URL u = new URL( "file:///" +filePath); response.setContentType(u.openConnection().getContentType()); response.setHeader( "Content-Disposition" , "inline; filename=" +f.getName()); //文件名應(yīng)該編碼成UTF-8 } else { //純下載方式 response.setContentType( "application/x-msdownload" ); response.setHeader( "Content-Disposition" , "attachment; filename=" + f.getName()); } OutputStream out = response.getOutputStream(); while ((len = br.read(buf)) 〉 0 ) out.write(buf, 0 ,len); br.close(); out.close(); } |