文件上传是一个很有意义的话题.
我将就 其原理和实际应用 以及安全的设计方面进行初步的讨论..
当一个form表单 被设置为enctype="MULTIPART/FORM-DATA" method="post" 时,其中的〈input type="file" name="filename" /> 标签如果被用户 选择了文件的话.
浏览器会把 文件内容连同 form的所有字段 格式化后传递到服务器~~
如下
一个测试:
<!--f.jsp-->
<%@ page contentType="text/html;charset=GB2312"%>
<HTML>
<BODY>
<p>选择要上传的文件:<BR>
<FORM action="accept.jsp" method="post" ENCTYPE="multipart/form-data">
<INPUT type=FILE name="boy" size="38">
<BR>
<INPUT type="hidden" id="tt" name="t" value="1">
<INPUT type="submit" id="gg" name="g" value="提交">
</BODY>
</HTML>
处理:
[color=green]
<!--accept.jsp-->
<%@ page contentType="text/html;charset=GB2312"%>
<%@ page import="java.io.*"%>
<HTML>
<BODY>
<%try{InputStream in=request.getInputStream();
File f=new File("f:\\qin\\jsp","a.txt");
FileOutputStream o=new FileOutputStream(f);
byte b[]=new byte[1024];
int n;
while((n=in.read(b))!=-1)
{o.write(b,0,n);
}
o.close();
in.close();
}
catch(IOException ee){}
out.print("文件已经上传");
%>
</body>
</HTML>
[/color]
accept.jsp 中从request流中读取所有字节保存为文本文件,
提交后 查看f:\qin\a.txt的内容如下:
-----------------------------7d631c72a0204
Content-Disposition: form-data; name="boy"; filename="C:\Documents and Settings\goodday\My Documents\2005sn.txt"
Content-Type: text/plain
安装方法
在“添加删除”里面选择删除"visual studio 2005",然后在打开的窗口中选择最后一项“添加注册码升级到正式版”,在里面输入下面的cd-key,然后点升级就OK了!
SN:KYTYH-TQKW6-VWPBQ-DKC8F-HWC4J
-----------------------------7d631c72a0204
Content-Disposition: form-data; name="t"
1
-----------------------------7d631c72a0204
Content-Disposition: form-data; name="g"
提交
-----------------------------7d631c72a0204--
其中-----------------------------7d631c72a0204为 为字段间的分隔符;
-----------------------------7d631c72a0204--为结束符.
分割符为42个字节,
前29个-,然后是浏览器标识,ie为7d,后面的11个随机的16进制一位数的字符.
以2进制的方式 获取request流后,就不能用getparameter(jsp)或form(asp)
的方式 读取参数的值了.
我们要重写 分析获取参数值的方法.
form字段每一个参数的名称和值的格式为:
Content-Disposition: form-data; name="t"
1
name="t" 为名称,两个\r\n(newline)后是其值
文件则是多一个filename=和一行Content-Type: text/plain
然后一样的2个newline后是2进制的 文件的所有内容
直到分割符...
------------------------
分析了 这些原理 我们就可以 实现文件上传功能~~~
就是 按分割符 读取request流;
然后 分析各段的 name和value
存入 hashtable;
如果是文件则 存入 文件.
-----------------------------------
jsp的例子:
文件上传类:UploadBean.java
package qin;
import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class UploadBean {
private String[] sourceFile = new String[255]; //源文件名
private String[] suffix = new String[255]; //文件后缀名
private String canSuffix = ".gif.jpg.jpeg.png.rar.txt"; //可上传的文件后缀名
private String objectPath = "f:/"; //目标文件目录
private String[] objectFileName = new String[255]; //目标文件名
private ServletInputStream sis = null; //输入流
private String[] description = new String[255]; //描述状态
private long size = 100 * 1024; //限制大小
private int count = 0; //已传输文件数目
private byte[] b = new byte[4096]; //字节流存放数组
private boolean successful = true;
private Hashtable fields = new Hashtable();
public UploadBean() {
}
//设置上传文件的后缀名
public void setSuffix(String canSuffix) {
this.canSuffix = canSuffix;
}
//设置文件保存路径
public void setObjectPath(String objectPath) {
this.objectPath = objectPath;
}
//设置文件保存路径
public void setSize(long maxSize) {
this.size = maxSize;
}
//文件上传处理程序
public void setSourceFile(HttpServletRequest request) throws IOException {
sis = request.getInputStream();
int a = 0;
int k = 0;
String s = "";
while ( (a = sis.readLine(b, 0, b.length)) != -1) {
s = new String(b, 0, a);
if ( (k = s.indexOf("filename=\"")) != -1) {
// 取得文件数据
s = s.substring(k + 10);
k = s.indexOf("\"");
s = s.substring(0, k);
sourceFile[count] = s;
k = s.lastIndexOf(".");
suffix[count] = s.substring(k + 1);
if (canTransfer(count)) {
transferFile(count);
}
++count;
} else if ( (k = s.indexOf("name=\"")) != -1) {
// 普通表单淙朐兀袢∈淙朐孛?nbsp;
String fieldName = s.substring(k+6, s.length()-3);
sis.readLine(b, 0, b.length);
StringBuffer fieldValue = new StringBuffer(b.length);
while ( (a = sis.readLine(b, 0, b.length)) != -1) {
s = new String(b, 0, a-2);
if ( (b[0] == 45) && (b[1] == 45) && (b[2] == 45) && (b[3] == 45) && (b[4] == 45)) {
break;
} else {
fieldValue.append(s);
}
}
fields.put(fieldName, fieldValue.toString());
}
if (!successful)
break;
}
}
//取得表单元素值
public String getFieldValue(String fieldName) {
if (fields == null ****** fieldName == null) {
return null;
}
return (String) fields.get(fieldName);
}
//取得上传文件数
public int getCount() {
return count;
}
//取得目标路径
public String getObjectPath() {
return objectPath;
}
//取得源文件名
public String[] getSourceFile() {
return sourceFile;
}
//取得目标文件名
public String[] getObjectFileName() {
return objectFileName;
}
//取得上传状态描述
public String[] getDescription() {
return description;
}
//判断上传文件的类型
private boolean canTransfer(int i) {
suffix[i] = suffix[i].toLowerCase();
//这个是用来传图片的,各位可以把后缀名改掉或者不要这个条件
if (sourceFile[i].equals("") ****** (!(canSuffix.indexOf("."+suffix[i])>=0))) {
description[i] = "ERR: File suffix is wrong.";
return false;
}
else {
return true;
}
}
//上传文件转换
private void transferFile(int i) {
String x = Long.toString(new java.util.Date().getTime());
try {
objectFileName[i] = x + "." + suffix[i];
FileOutputStream out = new FileOutputStream(objectPath + objectFileName[i]);
int a = 0;
int k = 0;
long hastransfered = 0; //标示已经传输的字节数
String s = "";
while ( (a = sis.readLine(b, 0, b.length)) != -1) {
s = new String(b, 0, a);
if ( (k = s.indexOf("Content-Type:")) != -1) {
break;
}
}
sis.readLine(b, 0, b.length);
while ( (a = sis.readLine(b, 0, b.length)) != -1) {
s = new String(b, 0, a);
if ( (b[0] == 45) && (b[1] == 45) && (b[2] == 45) && (b[3] == 45) && (b[4] == 45)) {
break;
}
out.write(b, 0, a);
hastransfered += a;
if (hastransfered >= size) {
description[count] = "ERR: The file " + sourceFile[count] +
" is too large to transfer. The whole process is interrupted.";
successful = false;
break;
}
}
if (successful) {
description[count] = "Right: The file " + sourceFile[count] +
" has been transfered successfully.";
}
out.close();
if (!successful) {
sis.close();
File tmp = new File(objectPath + objectFileName[count]);
tmp.delete();
}
}
catch (IOException ioe) {
description[i] = ioe.toString();
}
}
public static void main(String[] args) {
System.out.println("Test OK");
}
}
文件上传调用:Upload.jsp
〈%@ page contentType="text/html; charset=GB2312" %>
〈html>
〈head>
〈title>文件上载〈/title>
〈/head>
〈body>
〈form action="UploadSubmit.jsp" enctype="MULTIPART/FORM-DATA" method="post">
作者姓名:〈input type="text" name="Author" />
〈br />
公司名称:〈input type="text" name="Company" />
〈br />
文件描述:〈input type="text" name="Comment" />
〈br />
选择文件1:〈input type="file" name="filename1" />
〈br />
选择文件2:〈input type="file" name="filename2" />
〈br />
选择文件3:〈input type="file" name="filename3" />
〈br />
选择文件4:〈input type="file" name="filename4" />
〈br />
〈input type="submit" value="上载" />
〈/form>
〈/body>
〈/html>
文件上传提交:UploadSubmit.jsp
〈%@ page contentType="text/html;charset=gb2312"%>
〈jsp:useBean id="fileBean" scope="page" class="qin.UploadBean" />
〈%
fileBean.setObjectPath("D:\\");
fileBean.setSize(10000*1024);
fileBean.setSuffix(".gif.jpg.png.jpge.html.htm");
fileBean.setSourceFile(request);
String [] saSourceFile = fileBean.getSourceFile();
String [] saObjectFile = fileBean.getObjectFileName();
String [] saDescription = fileBean.getDescription();
int iCount = fileBean.getCount();
String sObjectPath = fileBean.getObjectPath();
for(int i=0;i〈iCount;i++) {
out.println("〈br>源始文件:");
out.println(saSourceFile[i]);
out.println("〈br>目标文件:");
out.println(sObjectPath+saObjectFile[i]);
out.println("〈br>上传说明:");
out.println(saDescription[i]);
out.println("〈br>");
}
out.println("〈br>作者:" + fileBean.getFieldValue("Author"));
out.println("〈br>公司:" + fileBean.getFieldValue("Company"));
out.println("〈br>说明:" + fileBean.getFieldValue("Comment"));
%>
Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1634106