服务器之家

服务器之家 > 正文

用java在web环境下上传和下载文件的技巧

时间:2020-03-22 13:01     来源/作者:mgqy

文件上传在web应用中非常普遍,要在jsp环境中实现文件上传功能是非常容易的,因为网上有许多用java开发的文件上传组件,本文以commons-fileupload组件为例,为jsp应用添加文件上传功能。

common-fileupload组件是apache的一个开源项目之一,可以从http://jakarta.apache.org/commons/fileupload/下载。

用该组件可实现一次上传一个或多个文件,并可限制文件大小。

下载后解压zip包,将commons-fileupload-1.0.jar复制到tomcat的webapps你的webappWEB-INFlib下,目录不存在请自建目录。

新建一个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"; // 临时文件目录
public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException
{
}
}

在doPost()方法中,当servlet收到浏览器发出的Post请求后,实现文件上传。以下是示例代码:

?
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();
// 设置最大文件尺寸,这里是4MB
fu.setSizeMax(4194304);
// 设置缓冲区大小,这里是4kb
fu.setSizeThreshold(4096);
// 设置临时目录:
fu.setRepositoryPath(tempPath);
 
// 得到所有的文件:
List fileItems = fu.parseRequest(request);
Iterator i = fileItems.iterator();
// 依次处理每一个文件:
while(i.hasNext()) {
FileItem fi = (FileItem)i.next();
// 获得文件名,这个文件名包括路径:
String fileName = fi.getName();
// 在这里可以记录用户和文件信息
// ...
// 写入文件,暂定文件名为a.txt,可以从fileName中提取文件名:
fi.write(new File(uploadPath + "a.txt"));
}
}
catch(Exception e) {
// 可以跳转出错页面
}
}

如果要在配置文件中读取指定的上传文件夹,可以在init()方法中执行:

?
1
2
3
4
5
6
7
8
9
public void init() throws ServletException {
uploadPath = ....
tempPath = ....
// 文件夹不存在就自动创建:
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,没有的话新建一个。

典型配置如下:

?
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后,启动tomcat,写一个简单的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时指定的url-pattern。

下面是某个大虾的代码:

这个Upload比smartUpload好用多了.完全是我一个个byte调试出来的,不象smartUpload的bug具多.

调用方法:

?
1
2
3
4
5
6
7
8
Upload up = new Upload();
up.init(request);
/**
此处可以调用setSaveDir(String saveDir); 设置保存路径
调用setMaxFileSize(long size)设置上传文件的最大字节.
调用setTagFileName(String)设置上传后文件的名字(只对第一个文件有效)
*/
up. uploadFile();

然后String[] names = up.getFileName(); 得到上传的文件名,文件绝对路径应该是

保存的目录saveDir+"/"+names[i];

可以通过up.getParameter("field"); 得到上传的文本或up.getParameterValues("filed")

得到同名字段如多个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(); //临时存放文件名的数据结构
private Hashtable parameter = new Hashtable(); //存放参数名和值的数据结构
private ServletContext context; //程序上下文,用于初始化
private HttpServletRequest request; //用于传入请求对象的实例
private String boundary = ""; //内存数据的分隔符
private int len = 0; //每次从内在中实际读到的字节长度
private String queryString;
private int count; //上载的文件总数
private String[] fileName; //上载的文件名数组
private long maxFileSize = 1024 * 1024 * 10; //最大文件上载字节;
private String tagFileName = "";
public final void init(HttpServletRequest request) throws ServletException {
this.request = request;
boundary = request.getContentType().substring(30); //得到内存中数据分界符
queryString = request.getQueryString();
}
public String getParameter(String s) { //用于得到指定字段的参数值,重写request.getParameter(String s)
if (parameter.isEmpty()) {
return null;
}
return (String) parameter.get(s);
}
public String[] getParameterValues(String s) { //用于得到指定同名字段的参数数组,重写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) { //设置上载文件要保存的路径
this.saveDir = saveDir;
File testdir = new File(saveDir); //为了保证目录存在,如果没有则新建该目录
if (!testdir.exists()) {
testdir.mkdirs();
}
}
public void setCharset(String charset) { //设置字符集
this.charset = charset;
}
public boolean uploadFile() throws ServletException, IOException { //用户调用的上载方法
setCharset(request.getCharacterEncoding());
return uploadFile(request.getInputStream());
}
private boolean uploadFile(ServletInputStream servletinputstream) throws //取得央存数据的主方法
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) { //如果一段分界符内的描述中有filename=,说明是文件的编码内容
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 + "字节!");
}
dos.write(buffer, 0, len);
}
dos.close();
}
else { //否则是字段编码的内容
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中临时文件名倒入数据中供用户调用
}
if (fileName.length == 0) {
return false; //如果fileName数据为空说明没有上载任何文件
}
return true;
}
private void put(String key, String value, Hashtable ht) {
if (!ht.containsKey(key)) {
ht.put(key, value);
}
else { //如果已经有了同名的KEY,就要把当前的key更名,同时要注意不能构成和KEY同名
try {
Thread.currentThread().sleep(1); //为了不在同一ms中产生两个相同的key
}
catch (Exception e) {}
key += "||||||||||" + System.currentTimeMillis();
ht.put(key, value);
}
}
/*
调用ServletInputstream.readLine(byte[] b,int offset,length)方法,该方法是从ServletInputstream流中读一行
到指定的byte数组,为了保证能够容纳一行,该byte[]b不应该小于256,重写的readLine中,调用了一个成员变量len为
实际读到的字节数(有的行不满256),则在文件内容写入时应该从byte数组中写入这个len长度的字节而不是整个byte[]
的长度,但重写的这个方法返回的是String以便分析实际内容,不能返回len,所以把len设为成员变量,在每次读操作时
把实际长度赋给它.
也就是说在处理到文件的内容时数据既要以String形式返回以便分析开始和结束标记,又要同时以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;
}
}
下载其实非常简单,只要如下处理,就不会发生问题。
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());
//文件名应该编码成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();
}
标签:

相关文章

热门资讯

玄元剑仙肉身有什么用 玄元剑仙肉身境界等级划分
玄元剑仙肉身有什么用 玄元剑仙肉身境界等级划分 2019-06-21
沙雕群名称大全2019精选 今年最火的微信群名沙雕有创意
沙雕群名称大全2019精选 今年最火的微信群名沙雕有创意 2019-07-07
男生常说24816是什么意思?女生说13579是什么意思?
男生常说24816是什么意思?女生说13579是什么意思? 2019-09-17
超A是什么意思 你好a表达的是什么
超A是什么意思 你好a表达的是什么 2019-06-06
华为nova5pro和p30pro哪个好 华为nova5pro和华为p30pro对比详情
华为nova5pro和p30pro哪个好 华为nova5pro和华为p30pro对比详情 2019-06-22
返回顶部