Servlet多文件上传
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 .>.F@...........
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 ...G@....q......
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; }}-----------------------------------------------------------
Servlet多文件上传的更多相关文章
- Servlet实现文件上传
一.Servlet实现文件上传,需要添加第三方提供的jar包 下载地址: 1) commons-fileupload-1.2.2-bin.zip : 点击打开链接 2) commons- ...
- Servlet实现文件上传,可多文件上传
一.Servlet实现文件上传,需要添加第三方提供的jar包 接着把这两个jar包放到 lib文件夹下: 二: 文件上传的表单提交方式必须是POST方式, 编码类型:enctype="mul ...
- 配置servlet支持文件上传
Servlet3.0为Servlet添加了multipart配置选项,并为HttpServletRequest添加了getPart和getParts方法获取上传文件.为了使Servlet支付文件上传需 ...
- jsp+servlet实现文件上传下载
相关素材下载 01.jsp <%@ page language="java" contentType="text/html; charset=UTF-8" ...
- java commons-fileupload servlet 多文件上传
commons-fileupload servlet 多文件上传 需要引入的 jar 包. commons-fileupload-1.3.2.jar commons-io-2.2.jar 工程路劲:查 ...
- servlet web文件上传
web文件上传也是一种POST方式,特别之处在于,需设置FORM的enctype属性为multipart/form-data. 并且需要使用文件域. servlet的代码比较关键是这几句: // 使用 ...
- 通过JSP+servlet实现文件上传功能
在TCP/IP中,最早出现的文件上传机制是FTP.它将文件由客户端到服务器的标准机制. 但是在JSP中不能使用FTP来上传文件,这是有JSP的运行机制所决定的. 通过为表单元素设置Method=&qu ...
- Servlet之文件上传
上传表单中的注意事项: 表单 method 属性应该设置为 POST 方法,不能使用 GET 方法 表单 enctype 属性应该设置为multipart/form-data 下面的实例是借助于com ...
- 使用FileUpload实现Servlet的文件上传
简介 FileUpload 是 Apache commons下面的一个子项目,用来实现Java环境下的文件上传功能. FileUpload链接 FileUpload 是基于Apache的Commons ...
- Servlet中文件上传下载
1.文件下载: package FileUploadAndDown; import java.io.FileInputStream; import java.io.IOException; impor ...
随机推荐
- Date日期操作
获取年月日时分秒: package com.util; import java.text.DateFormat; import java.util.Calendar; import java.util ...
- [Linux-vi] The simple set of vi command
Source : https://www.cs.colostate.edu/helpdocs/vi.html What is vi? The default editor that comes wit ...
- java 反射应用
场景需求最近的一次解析数据包中,因为协议有改变,本来的定长的包,现在变为不定长的.举个例子,本来协议中规定,一个包中,有8个标签,但是每次上来的,不一定都有8个,没有的话,硬件过来的都是0.同时里面也 ...
- 使用Postman验证TFS Rest API
概述 你可能已经了解到,TFS自2015版本发布以来,开始支持通过REST API的方式提供接口服务,第三方平台可以通过通用的HTTP协议访问TFS系统,获取数据.请求编译等.REST API在原有. ...
- Django:上传文件或者图片时request.FILES的值为空
在form表单中加上属性 enctype="multipart/form-data"
- net 串口通讯 网口通讯(Socket)
1.串口通讯 2.网口(Socket) 源码下载:源码附件
- .Net Core 自定义配置源从配置中心读取配置
配置,几乎所有的应用程序都离不开它..Net Framework时代我们使用App.config.Web.config,到了.Net Core的时代我们使用appsettings.json,这些我们再 ...
- F#语言入门之什么是F#语言
F#是一种函数式编程语言,可以轻松编写正确且可维护的代码. F#编程主要涉及定义类型推断和自动泛化的类型和函数. 这使您可以将焦点保留在问题域上并操纵其数据,而不是编程的细节. open System ...
- Flask 语音分析
1. 安装api 百度组件 pip install baidu-aip 2.登录百度ai账号 ,建立一个账号 http://ai.baidu.com/ from aip import Aip ...
- 【文文殿下】ExBSGS
无需逆元版本: #include<cstdio> #include<cassert> #include<cmath> #include<map> typ ...