发布日期:2023-03-31来源:武汉北大青鸟武汉校区作者:admin
方法 前面四个public类型的方法用于返回FileUploadBean对象的私有域,它们是:getFilepath,getFilename,getContentType以及getFieldValue。 public String getFilepath() 返回filepath私有域的值。 public String getFilename() 返回filename私有域的值。 public String getContentType() 返回contentType私有域的值。 public String getFieldValue(String fieldName) 返回HTML表单中指定输入元素的值,元素的名字通过fieldName参数指定。 public void setSavePath(String savePath) 用该方法指定服务器上保存上载文件的目录的名字。
public void doUpload(HttpServletRequest request) throws IOException doUpload是FileUploadBean类中重要的一个方法。它的任务有二个:,它从HTML表单提取出输入域的名字和值并保存到Dictionary对象;第二,doUpload方法提取出上载的文件,把这个文件保存到savePath指定的路径,并分别把文件的名字、路径、内容类型赋给filename、filepath和contentType域。 private void setContentType(String s) 由doUpload方法调用。setContentType方法从原始字节数据提取出上载文件的内容类型。 private void setFilename(String s) 由doUpload方法调用。setFilename方法从原始字节数据提取出文件路径和名字。 doUpload方法的参数是Servlet/JSP容器创建的HttpServletRequest对象。HttpServletRequest对象描述了程序为了提取出HTML表单元素名字-值对以及上载文件必须处理的HTTP请求。doUpload方法先通过HttpServletRequest对象的getInputStream方法获得ServletInputStream对象。 如前所述,每一个表单元素由分界符和一组回车换行符分隔。因此,我们可以一行一行地读入HttpServletRequest对象的内容。下面这行代码定义了一个名为line的byte数组:
然后,我们用ServletInputStream对象的readLine方法读入HttpServletRequest对象内容的行: int i = in.readLine(line, 0, 128); 行应该是分界符,而且如果没有错误的话,它的长度应该大于3。如果它的长度小于3,我们可以认为出现了错误,doUpload方法应该立即返回: if (i < 3) return; 分界符和分界符的长度都非常重要,从本文后面你可以看到这一点。分界符由一组回车换行符结束,因此它的实际长度要比readLine方法返回的字节数少2。 int boundaryLength = i - 2; 丢弃byte数组line的后2个回车换行符即可获得分界符: String boundary = new String(line, 0, boundaryLength); 接下来,fields域被实例化成Hashtable对象。这个Hashtable对象将用来保存HTML表单元素的名字/值对。 fields = new Hashtable(); 由于已经有了分界符,接下来我们就可以开始提取出表单元素的值。具体方法是用一个while循环按行读入HttpServletRequest对象的内容,直至遇到内容结束readLine方法返回-1为止。所有的表单元素都以分界符开始,后面跟上“Content-Disposition”行,这一行由下面这些字符开始: Content-Disposition: form-data; name= 表单元素有两种类型:文件,非文件(普通的表单元素,如TEXT或者HIDDEN元素)。这两种表单元素的区别在于文件元素包含字符串“filename="filename"”。由此,我们可以利用该信息把文件和非文件的表单输入元素区别开来,代码如下: if (newLine.startsWith("Content-Disposition: form-data; name=\"")) { if (newLine.indexOf("filename=\"") != -1) { // 文件型表单输入元素 // 这里加上提取文件的代码 . . . } else { // 普通表单输入元素 // 这里加上提取表单元素的代码 . . . }
现在,我们先来看看提取文件内容的代码。 文件路径包含在“Content-Disposition”的后面。为提取文件路径和文件名字,doUpload方法调用了setFilename私有方法。setFilename方法提取出文件路径和文件名字信息,然后把它们赋值给filepath和filename域。调用setFilename方法之后,filename域应该不再是null。如果此时filename域仍旧是null,则说明遇到了问题,doUpload方法直接返回。 if (filename==null) return; “Content-Disposition”行之后的下一行是内容类型行。因此,doUpload方法接着调用readLine方法,然后调用setContentType私有方法。setContentType方法和setFilename方法相似,它从原始字节数据中提取出上载文件的内容类型并保存到contentType域。 紧接内容类型行的下一行是空行,因此程序再调用了一次readLine方法。 i = in.readLine(line, 0, 128); 接下来开始了真正的文件内容。我们先应该做好通过PrintWriter对象把文件写入磁盘的准备。 PrintWriter pw = new PrintWriter(new BufferedWriter( new FileWriter( ( savePath==null? "" : savePath ) + filename ))); 上载文件保存到哪个位置取决于savePath域是否已经设置。如果savePath域没有设置,它的值是null,则文件将被保存到默认目录;如果savePath域已经设置,它的值不是null,则上载的文件被保存到它所指定的目录。 然后我们就可以提取文件的内容。具体方法是使用while循环,每次循环读入一行内容并通过PrintWriter的输出方法把它写入磁盘。但我们知道,文件的后一行包含两个回车换行符号,所以保存到磁盘的字节数据不应该包含这两个字符。因此,如果读入的行不是文件的后一行,我们把所有读到的字节数据写入磁盘;如果读入的行已经是文件的后一行,写入磁盘的字节数据要减去后两个字符。
然而,我们并不知道文件的大小,我们只知道紧接文件内容的下一行又是一个分界符;或者,如果文件是后一个HTML表单元素,接下来的一行是分界符加上两个短划线字符。因此,只要检查下一行内容是否是分界符,我们就知道了何时应该结束while循环。这就是前面说分界符很重要的原因,在这里我们必须用到分界符。 虽然我们可以读取下一行内容然后用startsWith方法检查它是否是一个分界符,然而,由于字符串操作的开销非常大,为了减少字符串操作,我们比较readLine读入的字节数组的长度。后者应该等于boundaryLength + 2;或者,如果它是HttpServletRequest对象中的后一行,由于多出了后两个短划线字符,它应该等于boundaryLength + 4。由于一行内容即使不是分界符也可以和分界符一样长,当长度匹配之后我们又将它与分界符比较。这就是前面提到boundaryLength很重要的原因了。 整个处理过程的实现代码如下: while (i != -1 && !newLine.startsWith(boundary)) { i = in.readLine(line, 0, 128); if ((i==boundaryLength+2 || i==boundaryLength+4) && (new String(line, 0, i).startsWith(boundary))) pw.print(newLine.substring(0, newLine.length()-2)); else pw.print(newLine); newLine = new String(line, 0, i); } 把文件内容保存到磁盘之后,我们关闭了PrintWriter。 pw.close(); 非文件的表单元素也可以用类似的方法提取。不同之处在于,此时我们不再把数据写入磁盘,而是把名字-值对保存到Dictionary对象。 fields.put(fieldName, fieldValue.toString()); byte[] line = new byte[128];
Copyright (c) 2006-2023 武汉宏鹏教育咨询有限公司 版权所有 All Rights Reserved.