客户端需要使用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复制文件的时候,最好是:读字节,写字节。