JSP/SERVLET上载的难点 支持任意格式任意数量的文件上载上载控制的实现表单信息的取得即插即用的应用方法我个人认为制约通用 上载组件的实现主要是这四个难点 JSP/SERVLET上传的原理 JSP/SERVLET文件 上载是通过ServletInputStream类来实现的ServletInputStream类是javaioInputStream的一个扩展抽象类实质上也是一个输入流通过ReadLine方法从Request端一行一行读取可见JSP/SERVLET上载根本上是用流来实现的理解了这个就不难理解整个 上载的原理ServletInputStream实现文件 上载必须采用HTTP POST或者HTTP PUT协议HTTP GET协议只能传递很少的数据是不能实现文件上载的 下面我们来看一下上传的数据流的结构首先要在BROWSER端给出一个请求我们的请求如下(文件名为testjsp): <%@ page contentType=text/html; charset=GBK %> <html> <head> <title>文件上载</title> </head> <body> <form action=testjsp enctype=MULTIPART/FORMDATA method=post> 说明一: <input type=text name=explain /> <br /> 说明二: <input type=text name=explain /> <br /> 请选择上载文件 <input type=file name=file /> <br /> 请选择上载文件 <input type=file name=file /> <br /> 说明三: <input type=text name=explain /> <br /> <input type=submit value= 上载 /> </form> </body> </html> 显示如下 说明一 说明二 请选择上载文件一 请选择上载文件二 说明三 在上载请求页中混杂了表单的三个输入框及两个上载文件当然输入框可以更多更杂可以有选择框单选及多选按钮待上传的文件也可以有三个四个或所需要的更多 action=testjsp表示表单将提交到testjsp另外注意表单属性中必须要有这句enctype=MULTIPART/FORMDATAenctype指定 Form 输入资料的编码方式method属性必须为post这样表单才能提交大量数据也表示本表单的数据传递将用流操作method=get表示数据将通过地址栏进行传递虽然方便快捷但只适合很少的数据量 响应端testjsp页面如下 <%@ page contentType=text/html; charset=GBK %> <html> <head> <title>文件上载</title> </head> <body> <jsp:useBean id=upBean scope=page class=comuploadUpBean/> <% upBeandoUpload(request); outprintln(上载已完成请查看输出文件); %> </body> </html> testjsp收到请求后调用一个java bean执行doUpload(request)操作本操作将完成流(unicode格式)的接收并不做任何处理地将流顺序写入一个文本文件里读写操作中用了一个缓沖区byte[] readByte用了一个ServletInputStream 的一个方法readLine(byte[] b int offint len)方法读取流请大家注意ServletInputStream 流的read Line方法是一次读入指定大小的行java bean (UpBeanjava)代码如下 [code]package comupload; import javaio*; import javaxservlet*; import javaxservlethttpHttpServletRequest; import javaxservletServletInputStream; public class UpBean { public void doUpload(HttpServletRequest req) throws ServletException IOExcept ion{ //首先定义一个文本文件 File file = new File(outtxt); //readCount 记录从输入流中实际读取的字符数 int readCount; //输入流缓沖区 byte[] readByte = new byte[]; //初始化输入流 ServletInputStream servletInputStream = reqgetInputStream(); //初始化一个输出流(到文件) FileOutputStream fileOutputStream = new FileOutputStream(file); //循环从读取输入流中读取字节 readCount = servletInputStreamreadLine(readByte readBytelength); while(readCount != ){ fileOutputStreamwrite(readBytereadCount); readCount = servletInputStreamreadLine(readByte ); } //关闭文件流 fileOutputStreamflush(); } }[/code] 为了便于我们阅读流上载的两个文件为两个简单的文本文件(有格式的文件不便于直接分析)onetxt及twotxt文件内容如下实验时请建立对应文本文件内容请直接copy以下所示 onetxt: one one one one one one twotxt two two two two two two 都准备好以后我们就可以运行了运行时请注意文本部分及文件部分最好不要出现汉字或其他双字符集字符尽量采用英文因为接收是采用的Unicode字符集我们未对输入做任何处理 我们在三个输入框输入的字符为说明一explain说明二explain说明三explainouttxt接收到如下字符 dae ContentDisposition: formdata; name=explain explain dae ContentDisposition: formdata; name=explain explain dae ContentDisposition: formdata; name=file; filename=C:\test\onetxt ContentType: text/plain one one one one one one dae ContentDisposition: formdata; name=file; filename=C:\test\twotxt ContentType: text/plain two two two two two two dae ContentDisposition: formdata; name=explain explain dae 可以很明显的看到outtxt被dae分成了五 节即表单的每个输入部分都对应一节结尾部分是dae刚好比开始的一段字符在最后多出两个 每节的第一行是输入内容的说明ContentDisposition: formdataname=explain表示 上载请求项的name文本输入部分仅这两个说明如果输入的是文件还用两项说明filename=C:\test\onetxt表示输入源基于mswindows的ie上载带有完整的路径netscape及其他浏览器可能只有一个文件名还有一项是关于输入格式的ContentType:text/plain表示输入格式是文本类型如果我们上载的是bmp文件则为ContentType: image/bmpword文件为application/msword说明的下面紧接着是一个空行然后下面才是我们所需的内容 仔细分析未加修改的输入流格式有助于我们实现文件与输入文本的准确分离 通过以上的分析可以看出准确分离上载的文件及文本信息需要以下要素数据段分割符结束符(比分割符多出两个)输入文本及上载的文件区分标志(文本为name=文件为filename=)编码格式可以通过HttpServletRequest 类getCharacterEncoding() 方法取得表单文本部分名称及内容名称为name=后面的字符内容为该段第三行及以后的内容文件名称及内容名称为filename=后面的字符内容为该段第三行及以后的内容 下面我们将讨论输入流的分离 程序实现分析 我们首先画出程序实现的主体结构图请注意判断文件标志(indexOf(filename=)>)与判断文本标志(indexOf(name=)>)的顺序当(indexOf(filename=)>)成立时(indexOf(name=)>)一定也是成立的所以判断文件要在判断文本前 分离文件及输入文本为完整保存上载的文件信息及输入的文本信息本程序建立了两个类public class FileInfo public class InputField及两个线性表private ArrayList upFilesListprivate ArrayList inputFieldList用于动态增加文件信息及文本信息定义如下 FileInfojava(记录上载文件信息) package comupload; public class FileInfo { private String fileName; private boolean validFlag; private String filePath; private long fileSize; //设置文件信息 //上载文件是否有效标志 public void setValidFlag(boolean validFlag){ thisvalidFlag = validFlag; } //文件名 public void setFileName(String filename){ thisfileName = filename; } //存贮路径 |