JSP上传文件浅析

客户端需要使用form表单,method设为post,enctype默认为application/x-www-form-urlencoded,这里改为multipart/form-data,把input标签的type属性设为file,这就是RFC 1867协议(Form-based File Upload in HTML)。代码如下:

<body>
  <h4>请选择文件...</h4>
  <form action="upload.jsp" method="post" enctype="multipart/form-data">
    <input type="text" name="username" value="Lily"/>
    <input type="file" name="filename"/>
    <input type="text" name="age" value=30 />
    <input type="submit" />
  </form>
 </body>

用户选择文件提交表单后,服务器端就可以通过JSP的内置对象request(实际上是HttpServletRequest的实例)的输入流获取用户表单内容。需要注意的是此输入流不仅包含了用户上传的文件,还包含了表单的其他字段信息,我们可以把此输入流存为临时文件,然后从临时文件中提取用户真正上传的文件。

注:将enctype改为multipart/form-data后,服务端通过request.getParameter(inputname)得不到input的值。如果想得到input的值,需要修改提交方式,如下:

<script type="text/javascript">
		function upload(){
			document.forms.myform.action = "upload.jsp?username=Lily&age=30";
			document.forms.myform.submit();
		}
	</script>
<form id="myform" method="post" enctype="multipart/form-data">
  	<input type="text" name="username" value="Lily"/>
    <input type="file" name="filename"/>
    <input type="text" name="age" value=30 />
    <input type="button" οnclick="upload();" value="提交"/>
  </form>

获取输入流存为临时文件代码:

ServletInputStream input = request.getInputStream();//获取输入流
String sysPath = new File(application.getRealPath("")).getPath();
out.println(sysPath);
String tempFilePath = sysPath + "\\temp.temp";//临时文件路径
OutputStream output = new FileOutputStream(tempFilePath);//创建输出流
byte[] b = new byte[1024];
int c = 0;
while((c = input.read(b)) > -1){
    output.write(b, 0, c);
}
output.close();
input.close();


临时文件路径自己定义,客户端文件名hello.txt,文件内容如下:

Wellcom to China,Tom!
Wellcom to China,Jim!

临时文件内容如下:

------WebKitFormBoundaryh79CZqB015K8AHBJ
Content-Disposition: form-data; name="username"

Lily
------WebKitFormBoundaryh79CZqB015K8AHBJ
Content-Disposition: form-data; name="filename"; filename="hello.txt"
Content-Type: text/plain

Wellcom to China,Tom!
Wellcom to China,Jim!
------WebKitFormBoundaryh79CZqB015K8AHBJ
Content-Disposition: form-data; name="age"

30
------WebKitFormBoundaryh79CZqB015K8AHBJ--
我们可以看到临时文件多了很多内容,不但包含了上传文件的内容,还包含了表单其他字段的信息。临时文件多处出现了相同的字符串,它们是每个字段信息开始的标识。字符串的内容是"------WebKitFormBoundary" + 16个随机生成的字符,前面部分是固定的,后面16个字符每次上传都会不同。最后一个标识多了"--",表示文件结束。(这些相同的字符串就是字段分隔符,是由浏览器产生的,不同的浏览器产生的规则不同。)其中有个字段信息多了filename属性,并且多了一行,表示上传文件的类型,这个字段就表示上传的文件。有了这些信息,我们就能提取用户上传的文件了。下面是提取文件的代码:

//从临时文件中提取上传的文件
RandomAccessFile randomFile = new RandomAccessFile(tempFilePath, "r");
RandomAccessFile fileOutput = null;
long length = randomFile.length();
final String firstLine = randomFile.readLine();
final String lastLine = firstLine + "--";
randomFile.seek(0);
    while(randomFile.getFilePointer() < length){
    String line = randomFile.readLine();
    if(line.equals(firstLine)){
    	String first = randomFile.readLine();
    	if(first.indexOf("filename=\"") > -1 
    		&& (line = randomFile.readLine()).indexOf("Content-Type:") > -1){
   		int beginIndex = first.indexOf("filename=\"") + 10;
   		String filename = first.substring(beginIndex, first.length() - 1);//获取文件名
   		fileOutput = new RandomAccessFile(sysPath + "\\" + filename, "rw");
   		randomFile.readLine();//空行
   		line = randomFile.readLine();
   		boolean f = false;
   		do{
   			String nextLine = randomFile.readLine();
   			if(!nextLine.equals(firstLine) && !nextLine.equals(lastLine)){//下一行是否结束
   				line = line + "\r\n";// readLine方法读取的行不包括回车和换行,所以需要加上
   				f = true;
   			} else {
   				f = false;
   			}
   			fileOutput.writeBytes(line);
   			line = nextLine;
   		}while(f);
   		break;
    	} else 
    		continue;
    }
    continue;
}
randomFile.close();
fileOutput.close();
out.println("<br/>upload success!");

临时文件中有个Content-Type属性,表示上传文件的类型。限于本人水平,以上方法只能上传文本类型的文件(中文也行),对于其他类型的文件无能为力,希望有高人指点。


通过摸索,终于知道,以上方法为什么不能上传其他类型的文件了,原来是对Java IO的了解有限,经过改进代码,可以上传任何类型的文件了,不过可能大小有限制。代码如下:

ServletInputStream input = request.getInputStream();//获取输入流
    		String sysPath = new File(application.getRealPath("")).getPath();
    		out.println(sysPath);
    		String tempFilePath = sysPath + "\\temp.temp";//临时文件路径
    		OutputStream output = new FileOutputStream(tempFilePath);//创建输出流
    		byte[] b = new byte[1024];
    		int c = 0;
    		while((c = input.read(b)) > -1){
    			output.write(b, 0, c);
    		}
    		output.close();
    		input.close();
    		
    		//从临时文件中提取上传的文件
    		RandomAccessFile randomFile = new RandomAccessFile(tempFilePath, "r");
    		RandomAccessFile fileOutput = null;
    		long length = randomFile.length();
    		final String firstLine = randomFile.readLine();
    		final String lastLine = firstLine + "--";
    		randomFile.seek(0);
    		while(randomFile.getFilePointer() < length){
    			String line = randomFile.readLine();
    			if(line.equals(firstLine)){
    				String first = randomFile.readLine();
    				if(first.indexOf("filename=\"") > -1 
    						&& (line = randomFile.readLine()).indexOf("Content-Type:") > -1){
   						int beginIndex = first.indexOf("filename=\"") + 10;
   						String filename = first.substring(beginIndex, first.length() - 1);//获取文件名
   						fileOutput = new RandomAccessFile(sysPath + "\\" + filename, "rw");
   						randomFile.readLine();//空行
   						long startp = randomFile.getFilePointer();
   						out.println(startp);
   						long endp = startp;
   						line = randomFile.readLine();
   						while(!line.equals(firstLine) && !line.equals(lastLine)){
   							endp = randomFile.getFilePointer();//获得文件结束的位置
   							line = randomFile.readLine();
   						}
   						out.println(endp);
   						randomFile.seek(startp);//跳转到文件开始位置
   						byte[] bs = new byte[1024];
   						int count = 0;
   						while(randomFile.getFilePointer() < endp){
   							long l = endp - randomFile.getFilePointer();
   							if(l > 1024l){
   								count = randomFile.read(bs);
   							} else {
   								count = randomFile.read(bs, 0, (int)l) - 2;//去掉最后的回车换行,所以减2
   							}
   							fileOutput.write(bs, 0, count);
   						}
   						break;
    				} else 
    					continue;
    			}
    			continue;
    		}
    		fileOutput.close();
    		randomFile.close();
    		out.println("<br/>upload success!");
第一种方法之所以不行,是因为readLine()方法经过了解码,解码只支持纯文本(字符),对于其他的类型的文件则会解码成乱码。第二种方法读的时候是读取的字节,写的时候也是写入的字节,中间没有经过解码,也没有经过编码,所以就是完整的复制。所以通过Java复制文件的时候,最好是:读字节,写字节。



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值