Servlet多文件上传

各位大侠可能会对263电子邮箱中的"上传附件"功能有印象,就是:在浏览
器中点击"浏览",弹出一个对话框,选中文件后,单击"确定",文件就被上传到了服务器端。
因为需要,就到网上找了几个控件,如SmartUpload等,但都觉得不好用,或者
说是不合用,决定自己做一个。近日看到网上也有人提问怎么上载文件,于是把编制过程整理一遍,希望对大家有所帮助,不足之处,请多多指教。 准备
侦听工具,如SpyNet(包括CaptureNetPeepNet),目的是用于分析数据包格式;

Java环境:至少要包括一个Servlet引擎,一套JDK;如果没有,可以访问

"http://www.jsp001.com/article/Application_Server_Comparison_Matrix_20010226.html"

从这36款中随便找出一种来,安装运行即可。JSP服务器都会支持SERVLET,因为JSP本身就是先被编译成SERVLET再执行的。
过程


1、制作HTML页面,用于上传文件。需要注意:要指定enctype属性为"multipart/form-data",因为数据流的格式是不一样的。

<form
action="/java/servlet/powerise.nms.web.UploadFile"

method=post
enctype="multipart/form-data"> <p>

<input type=radio
name=type value=0>model

<input type=radio name=type
value=1>report <input name=id >

<input type=file name=file
value="test"> </p>

<input type=submit> </form>


2、HTML页面做好后,就可以开始分析数据流了。先打开侦听器,然后在浏览

器(IE,
Netscape)中打开本页面,随意选择一个文件,单击"确定",看看侦听器听到了什么。在跳过前面几个包后,可以得到下面这两个相关的包。

第一个包的很容易明白,在Servlet中,用getHeader(String)能得到的内容

就在这里面。不过这个包,用HttpServletRequest的getInputStream是得不到的。

关于HTTP协议的更多信息,可以访问www.w3c.org。

0000:
00 E0 4C DD 2F 4F 00 50 BA A6 C3 CF 08 00 45 00 ..L./O.P......E.

0010: 02
3E 01 46 40 00 80 06 0E F5 AC 12 C8 01 AC 12 .>[email protected]

0020: C8
58 04 12 00 50 48 82 2A 39 FA 97 28 31 50 18 .X...PH.*9..(1P.

0030: 44 70
A4 76 00 00 50 4F 53 54 20 2F 6A 61 76 61 Dp.v..POST /java

0040: 2F 73 65
72 76 6C 65 74 2F 69 6D 63 2E 55 70 6C /servlet/imc.Upl

0050: 6F 61 64 20
48 54 54 50 2F 31 2E 31 0D 0A 41 63 oad HTTP/1.1..Ac

0060: 63 65 70 74 3A
20 69 6D 61 67 65 2F 67 69 66 2C cept: image/gif,

0070: 20 69 6D 61 67 65
2F 78 2D 78 62 69 74 6D 61 70 image/x-xbitmap

0080: 2C 20 69 6D 61 67 65
2F 6A 70 65 67 2C 20 69 6D , image/jpeg, im

0090: 61 67 65 2F 70 6A 70 65
67 2C 20 61 70 70 6C 69 age/pjpeg, appli

00A0: 63 61 74 69 6F 6E 2F 76 6E
64 2E 6D 73 2D 70 6F cation/vnd.ms-po

00B0: 77 65 72 70 6F 69 6E 74 2C 20
61 70 70 6C 69 63 werpoint, applic

00C0: 61 74 69 6F 6E 2F 76 6E 64 2E 6D
73 2D 65 78 63 ation/vnd.ms-exc

00D0: 65 6C 2C 20 61 70 70 6C 69 63 61 74
69 6F 6E 2F el, application/

00E0: 6D 73 77 6F 72 64 2C 20 2A 2F 2A 0D 0A
52 65 66 msword, */*..Ref

00F0: 65 72 65 72 3A 20 68 74 74 70 3A 2F 2F 31
37 32 erer:http://172

0100: 2E 31 38 2E 32 30 30 2E 38 38 2F 64 65 6D 6F 2F
.18.200.88/demo/

0110: 74 65 73 74 2E 68 74 6D 6C 0D 0A 41 63 63 65 70
test.html..Accep

0120: 74 2D 4C 61 6E 67 75 61 67 65 3A 20 7A 68 2D 63
t-Language: zh-c

0130: 6E 0D 0A 43 6F 6E 74 65 6E 74 2D 54 79 70 65 3A
n..Content-Type:

0140: 20 6D 75 6C 74 69 70 61 72 74 2F 66 6F 72 6D 2D
multipart/form-

0150: 64 61 74 61 3B 20 62 6F 75 6E 64 61 72 79 3D 2D
data; boundary=-

0160: 2D 2D 2D 2D 2D 2D 2D 2D 2D 2D 2D 2D 2D 2D 2D 2D
----------------

0170: 2D 2D 2D 2D 2D 2D 2D 2D 2D 2D 37 64 31 33 35 32
----------7d1352

0180: 61 32 30 31 36 63 0D 0A 41 63 63 65 70 74 2D 45
a2016c..Accept-E

0190: 6E 63 6F 64 69 6E 67 3A 20 67 7A 69 70 2C 20 64
ncoding: gzip, d

01A0: 65 66 6C 61 74 65 0D 0A 55 73 65 72 2D 41 67 65
eflate..User-Age

01B0: 6E 74 3A 20 4D 6F 7A 69 6C 6C 61 2F 34 2E 30 20
nt: Mozilla/4.0

01C0: 28 63 6F 6D 70 61 74 69 62 6C 65 3B 20 4D 53 49
(compatible; MSI

01D0: 45 20 35 2E 30 31 3B 20 57 69 6E 64 6F 77 73 20 E
5.01; Windows

01E0: 4E 54 20 35 2E 30 29 0D 0A 48 6F 73 74 3A 20 31 NT
5.0)..Host: 1

01F0: 37 32 2E 31 38 2E 32 30 30 2E 38 38 0D 0A 43 6F
72.18.200.88..Co

0200: 6E 74 65 6E 74 2D 4C 65 6E 67 74 68 3A 20 34 30
ntent-Length: 40

0210: 39 0D 0A 43 6F 6E 6E 65 63 74 69 6F 6E 3A 20 4B
9..Connection: K

0220: 65 65 70 2D 41 6C 69 76 65 0D 0A 43 6F 6F 6B 69
eep-Alive..Cooki

0230: 65 3A 20 4A 53 45 53 53 49 4F 4E 49 44 3D 63 74 e:
JSESSIONID=ct

0240: 71 62 76 65 38 73 35 31 0D 0A 0D 0A
qbve8s51....

再看第二个包,可以看到,所要传的参数都在。下文只分析这个包。

0000: 00 E0 4C DD 2F
4F 00 50 BA A6 C3 CF 08 00 45 00 ..L./O.P......E.

0010: 01 C1 01 47 40 00
80 06 0F 71 AC 12 C8 01 AC 12 [email protected]

0020: C8 58 04 12 00 50 48
82 2C 4F FA 97 28 31 50 18 .X...PH.,O..(1P.

0030: 44 70 3D AE 00 00 2D 2D
2D 2D 2D 2D 2D 2D 2D 2D Dp=...----------

↑(1)开始

0040: 2D 2D 2D 2D
2D 2D 2D 2D 2D 2D 2D 2D 2D 2D 2D 2D ----------------

0050: 2D 2D 2D 37 64
31 33 35 32 61 32 30 31 36 63 0D ---7d1352a2016c.

0060: 0A 43 6F 6E 74 65
6E 74 2D 44 69 73 70 6F 73 69 .Content-Disposi

0070: 74 69 6F 6E 3A 20 66
6F 72 6D 2D 64 61 74 61 3B tion: form-data;

0080: 20 6E 61 6D 65 3D 22 74
79 70 65 22 0D 0A 0D 0A name="type"....

0090: 30 0D 0A 2D 2D 2D 2D 2D 2D
2D 2D 2D 2D 2D 2D 2D 0..-------------

↑(2)第1段结束

00A0: 2D 2D 2D 2D
2D 2D 2D 2D 2D 2D 2D 2D 2D 2D 2D 2D ----------------

00B0: 37 64 31 33 35
32 61 32 30 31 36 63 0D 0A 43 6F 7d1352a2016c..Co

00C0: 6E 74 65 6E 74 2D
44 69 73 70 6F 73 69 74 69 6F ntent-Dispositio

00D0: 6E 3A 20 66 6F 72 6D
2D 64 61 74 61 3B 20 6E 61 n: form-data; na

00E0: 6D 65 3D 22 69 64 22 0D
0A 0D 0A 36 37 38 0D 0A me="id"....678..

00F0: 2D 2D 2D 2D 2D 2D 2D 2D 2D
2D 2D 2D 2D 2D 2D 2D ----------------

↑(3)第2段结束

0100: 2D 2D 2D 2D
2D 2D 2D 2D 2D 2D 2D 2D 2D 37 64 31 -------------7d1

0110: 33 35 32 61 32
30 31 36 63 0D 0A 43 6F 6E 74 65 352a2016c..Conte

0120: 6E 74 2D 44 69 73
70 6F 73 69 74 69 6F 6E 3A 20 nt-Disposition:

0130: 66 6F 72 6D 2D 64 61
74 61 3B 20 6E 61 6D 65 3D form-data; name=

0140: 22 66 69 6C 65 22 3B 20
66 69 6C 65 6E 61 6D 65 "file"; filename

0150: 3D 22 43 3A 5C 43 4F 4E 46
49 47 2E 53 59 53 22 ="C:CONFIG.SYS"

0160: 0D 0A 43 6F 6E 74 65 6E 74 2D
54 79 70 65 3A 20 ..Content-Type:

0170: 74 65 78 74 2F 70 6C 61 69 6E 0D
0A 0D 0A 73 68 text/plain....sh

0180: 65 6C 6C 3D 63 3A 5C 63 6F 6D 6D 61
6E 64 2E 63 ell=c:command.c

0190: 6F 6D 20 2F 70 20 2F 65 3A 33 32 30 30
30 0D 0A om /p /e:32000..

01A0: 0D 0A 2D 2D 2D 2D 2D 2D 2D 2D 2D 2D 2D 2D
2D 2D ..--------------

↑(4)第3段结束

01B0: 2D 2D 2D 2D 2D 2D 2D 2D 2D
2D 2D 2D 2D 2D 2D 37 ---------------7

01C0: 64 31 33 35 32 61 32 30 31 36
63 2D 2D 0D 0A d1352a2016c--..

(5)结束 ↑

以下对各标号作出说明:
(1)开始,这是整个能得到的输入流的开端;

(2)第1段结束。每一段包含一个参数的信息,这些信息包括类型、名称、内容等。
(3)和(4)与(2)是一样的。

(4)以后就是输入流的结束标志:boundary。 (5)为从输入流中能读到的最后一个字符。


注意了第一个包中,有一项叫做"boundary"。顾名思义,这个boundary是

"分界"标志了。每一段的开头都会有一个boundary,然后是
0D 0A,然后是一些

相关信息,接着是 0D 0A 0D
0A,紧跟着参数的实际值,然后是下一个boundary,

标志着下一段的开始。而整个输入流呢,以一个boundary结束。如果只有一个参数,那输入流的结构应该是下面这样的:

boundary
0D 0A ... 0D 0A 0D 0A ... boundary 0D 0A

↑ ↑ ↑ ↑

开始 参数的信息 参数的内容
结束

明白了数据流的结构,编程就简单了,以下给出一段源程序。该程序易于使用,(当然,也不必交版权费啦……)。先给出如下的调用示例,而把源程序附于末尾。

public
void doPost(HttpServletRequest req, HttpServletResponse resp)

throws
ServletException, java.io.IOException
{

//新建一个对象,其实,若写成static的,连这一步都可省了

DecodeRequestStream decode = new
DecodeRequestStream();

//调用Decode方法,返回一个哈希表 Hashtable hashtable =
decode.Decode(req, 2); ......

//获取type的值 String type =
(String)hashtable.get("type"); //获取id的值

String id =
(String)hashtable.get("id"); //以字节数据的方式获得文件的内容

byte[] filecontent =
(byte[])hashtable.get("file"); ...... }

Decode函数的声明如下: 入参:
(1)HttpServletRequest: 从这个参数中可以得到输入流;

(2)int ParamsCount:
这个参数表示输入流中除文件外,普通参数的个数

提供这个参数是从性能的角度出发的,下文中会有说明;
出参:

一个哈希表。如果是普通参数,则以(string name, string
value)的方式

保存,如果是文件,则以(string name, byte[] value)的方式保存;


对DecodeRequestStream类,作如下说明:
1、本类一次只能处理一个文件的上载。如果有多个文件,将会保存在一个字

节数组里面。实际上,可以很容易地把本程序改写成支持多文件的。我这
么做也是从性能方面考虑;个人认为,已经够用了;

2、文件必须是作为最后一个参数。此前有多少个参数必须在调用时通过Param

sCount参数指定。细心的大侠会发现这个参数也是为了性能。因为确定边

界boundary的位置是一个很费时的操作,需要先拷贝某个位置起与boundary

相同长度的字节数组,然后再与boundary比较。在确定文件内容的结束位置

时,要从文件流的开始处一直搜索到文件的结束处,对于大的文件,这是

很费时的。所以本程序中做了一点小动作,那就是,对于第ParamsCount+1

的那个参数(也就是文件参数),不用常规方法搜索,而是直接跳到输入流的

末尾(末尾是boundary
0D 0A),再往前倒数boundary的长度外加4个字节。

然后从这个位置开始定位boundary(一找一个准)。程序中,用了5个字节,
是"留有余地"的想法,其实不用。

3、本程序在 Tomcat 3.2.1 + Sun JDK 1.3.0_02
下运行通过,客户端浏览器

为Internet Exploere 5.0、Netscape Communacator 4.77 和Netscape
6。以下是源程序:

DecodeRequestStream.java

---------------------------------------------------------------------------

import
javax.servlet.*;import javax.servlet.http.*;import java.io.*;

import
java.util.*;public class DecodeRequestStream{

public Hashtable
Decode(HttpServletRequest req, int paramcount)

throws java.io.IOException
{ byte[] body = null; int bodyLen = 0;

byte[] bound = null; int boundLen
= 0; int index = 0; int count = 0;

bodyLen = req.getContentLength();
body = new byte[bodyLen];

BufferedInputStream dataIn= new
BufferedInputStream( req.getInputStream());

int readed = 0; int cur_read
= 0; while (readed < bodyLen)

{ cur_read = dataIn.read(body, readed,
bodyLen-readed);

if (cur_read <0) { break; }

readed = readed +
cur_read; } int i = 0;

while (i <= bodyLen) {

if (body[i] == 13
&& body[i+1] == 10) break; else

i ++; } if (i > bodyLen)
return null;

boundLen = i; bound = new byte[boundLen];

for (int
j=0; j<boundLen; j++) {

bound[j] = body[j + index]; //decode bound
}

i = i+2; //plus 2 to skip the following bytes "0D 0A"

index = i;
//point to the beginning of first parameter

Hashtable hashtable = new
Hashtable(); boolean moved = false;

while (i < bodyLen) { if (!moved
&& count == paramcount)

{

i = bodyLen-boundLen-5; //subst
more than 4, but little than 10

moved = true; }

if
(!compareByteArray(copybyte(body, i, boundLen), bound)) {

i++; } else {
count ++;

int j = index;

while ((j < i) && (body[j] !=
13 || body[j+1] != 10 ||

body[j+2]!=13 || body[j+3] != 10)) { j
++;

} if (j >= i) break;

String paramHeader = new String(body,
index, j-index+2);

index = j; int m =
paramHeader.indexOf("name="");

if (m < 0) break; m = m+6; //point to
name value

int n = paramHeader.indexOf(""", m); if (n <= m)
break;

String name = paramHeader.substring(m, n); //get
name

boolean isFile = false; String filename = "";

String filetype
= "";

m = paramHeader.indexOf("filename="", n+1); if (m > n)

{
isFile = true; m = m+10; //skip (filename=")

n = paramHeader.indexOf(""",
m);

if (n > m) filename = paramHeader.substring(m, n);

m =
paramHeader.indexOf("Content-Type: ", n+1); if (m > n)

{ m = m+14; n
= m;

while ((n < paramHeader.length())

&&
(paramHeader.charAt(n) != 13

|| paramHeader.charAt(n+1) != 10)) {
n++;

} if (n <= paramHeader.length())


filetype=paramHeader.substring(m, n); } }/*

status: j point to
the start of end flag (0D 0A 0D 0A) of current parameter′s

header after
j + 0D 0A 0D 0A, is the start of current parameter′s value

(byte format)
i point to the start of next boundary, that is,

"(current header) 0D 0A
0D 0A (current value) 0D 0A (next boundary)"

↑ ↑ ↑

index j
i

the following code gets current value*/

j = j+4; //skip 0D 0A 0D
0A, point to parameter value;

byte[] value = copybyte(body, j, i-j-2); if
(!isFile)

{ String tmpstr = new String(value);

hashtable.put(name,
tmpstr); } else {

hashtable.put(name, value); break; }

i = i +
boundLen + 2; index = i; } //end else

} //end while dataIn.close();
return hashtable; }

public boolean compareByteArray(byte[] a, byte[] b)
{

if (a.length != b.length) return false; for (int i=0; i<a.length;
i++)

if (a[i] != b[i]) return false; return true; }

public byte[]
copybyte(byte[] a, int from, int len) { int copylen = len;

if
((a.length-from) < copylen) copylen = a.length-from;

byte[] b = new
byte[copylen]; for (int i=0; i<copylen; i++) b[i] = a[from+i];

return
b; }}----------------------------------------------------------- 

-----------------------------------------------------------------------------------------------------------------------------

来源于:http://blog.itpub.net/47598/viewspace-23046

Servlet多文件上传

时间: 2024-10-10 15:42:43

Servlet多文件上传的相关文章

Servlet实现文件上传,可多文件上传

一.Servlet实现文件上传,需要添加第三方提供的jar包 下载地址: 1) commons-fileupload-1.2.2-bin.zip      :   点击打开链接 2) commons-io-2.3-bin.zip                       :    点击打开链接 接着把这两个jar包放到 lib文件夹下: 二: 文件上传的表单提交方式必须是POST方式, 编码类型:enctype="multipart/form-data",默认是 applicatio

JQuery和Servlet实现文件上传进度条,能显示上传速度,上传的百分比等

原文:JQuery和Servlet实现文件上传进度条,能显示上传速度,上传的百分比等 源代码下载地址:http://www.zuidaima.com/share/1550463319542784.htm JQuery和Servlet实现文件上传进度条,能显示上传速度,上传的百分比等

Servlet实现文件上传

一.Servlet实现文件上传,需要添加第三方提供的jar包 下载地址: 1) commons-fileupload-1.2.2-bin.zip      :   点击打开链接 2) commons-io-2.3-bin.zip                       :    点击打开链接 二.文件上传的表单提交方式必须是POST方式, 编码类型:enctype="multipart/form-data",默认是 application/x-www-form-urlencoded

jsp+servlet实现文件上传

上传(上传不能使用BaseServlet) 1. 上传对表单限制 * method="post" * enctype="multipart/form-data" * 表单中需要添加文件表单项:<input type="file" name="xxx" /> <form action="xxx" method="post" enctype="multipart/

Servlet的文件上传

以下内容引用自http://wiki.jikexueyuan.com/project/servlet/file-uploading.html: Servlet可以与HTML form标签一起使用允许用户将文件上传到服务器.上传的文件可以是文本文件或图像文件或任何文档. 一.创建一个文件上传表单 下述HTML代码创建了一个文件上传表单.以下是需要注意的几点: 表单method属性应该设置为POST方法且不能使用GET方法. 表单enctype属性应该设置为multipart/form-data.

jsp使用servlet实现文件上传

1.在index.jsp中写入以下代码 <form method="post" action="demo3" enctype="multipart/form-data"> 选择一个文件: <input type="file" name="uploadFile"/> <br/><br/> <input type="submit" val

servlet实现文件上传,预览,下载和删除

CreateTime--2017年9月4日09:24:59 Author:Marydon 一.准备工作: 1.1 文件上传插件:uploadify: 1.2 文件上传所需jar包:commons-fileupload-1.3.1.jar和commons-io-2.2.jar 1.3 将数据转成JSON对象需要jar包:commons-beanutils-1.8.3.jar.commons-collections-3.2.1.jar.commons-lang-2.6.jar.commons-log

使用WebUploader和servlet实现文件上传

注意点: 1.使用注解@MultipartConfig将一个Servlet标识为支持文件上传. 2.Servlet3.0将multipart/form-data的POST请求封装成Part,通过Part对上传的文件进行操作. 3.WebUploader参照网上教程实现. 4.引入webuploader.css.webuploader.js.Uploader.swf upload.jsp: <%@ page language="java" contentType="tex

servlet多文件上传(带进度条)

需要commons-fileupload-1.3.jar和commons-io-2.4.jar的支持 页面效果:(图片文件都可以) (1)进度标识类 public class UploadStatus { private long bytesRead; private long contentLength; private int items; private long startTime = System.currentTimeMillis(); //set get 方法 省略 } (2)监听