📄 multirequest.java
字号:
/**
* 带最大文件长度限制上传。如果因文件超长,本方法确保此时写入输出流的数据长度
* 刚好为maxFileSize个字节。允许再次调用upload()方法继续超长部分的数据。超长
* 文件不上传完,将获得不了后续参数。
* @param os 文件写入的流。
* @param maxFileSize 最大文件长度,整数最大值大约是2G,应该足够了。
* @return 成功返回文件大小,出错返回小于0的错误号,其中:
* 文件超过最大限制返回-1;上传过程中流结束返回-2;
* 上传文件前流已经结束返回-3。
* @throws IOException 从请求中读数据或往输出流里写数据时出现异常。
*/
public int upload(OutputStream os,int maxFileSize) throws IOException {
int fileLength = 0;
int safeLength = BUFFER_SIZE - boundary.length;
if (inputFinished)
return -3;
for (;;) { //循环从输入流中读,往输出流里写,直到读到边界为止
int n = fillTo(boundary);
if (n==-1) { //缓冲区被读满
if (fileLength+safeLength > maxFileSize) { //将会超大
safeLength = maxFileSize - fileLength;
os.write(buffer,0,safeLength);
moveFront(safeLength);
return -1;
} else { //不会超大
os.write(buffer,0,safeLength);
moveFront(safeLength);
fileLength += safeLength;
}
} else if (n==-2) { //输入流已经结束
return -2;
} else { //读到边界
if (fileLength+safeLength > maxFileSize) { //将会超大
safeLength = maxFileSize - fileLength;
os.write(buffer,0,safeLength);
moveFront(safeLength);
return -1;
} else { //不会超大
os.write(buffer,0,n);
moveFront(n+boundary.length);
fileLength += n;
filename = null;
fileFullName = null ;
while (parseNext()); //解析下一个参数直到不能解析为止
}
return fileLength;
}
} //for(;;)结束
}
/**
* 解析下一个参数片段,并将解析出的参数名、参数值放到参数哈希表中,若当前位置
* 指向一个上传文件的内容,则什么也不做,直接返回。也就是说必须处理完上传文件
* 才能继续对其后面的内容进行解析。
* @return 成功解析出下一个片段,则返回true;
* 输入流结束或遇到一个需要上传的文件,则不做任何处理,直接返回false。
* @throws IOException 读取请求输入流出现异常。
* @exception IndexOutOfBoundsException 协议格式不符合约定导致解析错误。
*/
private boolean parseNext() throws IOException{
if (inputFinished || (filename!=null && !filename.equals(""))) //已经不能解析了
return false;
String head = readString(HEAD_END); //读取片段头
if (head==null)
return false;
//从片段头中获得参数名
int nameBegin = head.indexOf("name=\"");
int nameEnd = head.indexOf((int)'\"',nameBegin+6);
String name = head.substring(nameBegin+6,nameEnd);
String value; //用来存放参数值
//判断是否文件类型
int fileBegin = head.indexOf("filename=\"");
if (fileBegin<0) { //不是文件类型,取出片段体作为参数值
value = readString(boundary);
} else { //是文件类型的参数,将文件名作为参数值
int fileEnd = head.indexOf((int)'\"',fileBegin+10);
value = head.substring(fileBegin+10,fileEnd);
//保存文件的全路径名
this.fileFullName = value ;
//去掉文件名中的路径
int f = value.lastIndexOf((int)'/');
if (f<0)
f = value.lastIndexOf((int)'\\');
if (f<0)
f = value.lastIndexOf((int)':');
if (f>=0)
value = value.substring(f+1);
filename = value;
}
//将参数加入列表
List values = (List) parameters.get(name);
if (values==null) { //如果还没有该名称的参数,则先建立一个空列表
values = new ArrayList();
parameters.put(name,values);
}
values.add(value);
return true;
}
/**
* 读输入流中的数据直到填满缓冲区,或者遇到指定的边界标记,或者输入流结束。
* 如果读取前在当前缓冲中找到边界标记,则不做任何事情,直接返回标记的位置。
* @param endFlag边界标记。
* return -1 缓冲区满;-2 输入流结束;遇到的第一个边界的位置。
*/
private int fillTo(byte[] endFlag) throws IOException{
boolean notFind = true;
int flagLength = endFlag.length;
int position = 0; //已经搜索过的位置
for(;;) { //循环查找
//从position到数据结束前范围内寻找边界
for (; position<dataLength-flagLength; position ++) {
notFind=false;
//和边界进行匹配
for (int i=flagLength-1;i>=0;i--) { //倒着找比较快
//只要有一个字节不相等position就不是匹配位置,进入position的下一个循环
if (buffer[position+i]!=endFlag[i]) {
notFind=true;
break;
}
}
if (!notFind) break; //找到了
}
//当前缓冲区已经搜索完成
if (notFind) { //如果没找到
if (dataLength<BUFFER_SIZE) { //没找到,并且缓冲区没满了
//读一段数据到缓冲区
int n = requestStream.read(buffer,dataLength,BUFFER_SIZE-dataLength);
if (n<0) {//没找到并且缓冲区没满,读时发现输入流结束了
inputFinished = true;
return -2;
}
dataLength += n;
continue; //读入了新数据,重新查找
} else //缓冲区满了并且没找到
return -1;
} else //如果找到了,则返回找到的位置
return position;
}
}
/**
* 将缓冲区中数据前移n字节,若n>=dataLength则将dataLength设置为0。
* @param n 前移多少字节。
*/
private void moveFront(int n) {
if (n>=dataLength)
dataLength = 0;
else {
System.arraycopy(buffer,n,buffer,0,dataLength-n);
dataLength -= n;
}
}
/**
* 读取以endFlag结尾的数据转换成字符串(不包括结束标记本身),
* 并将缓冲中内容连同结束标记全部清除。
* @param endFlag 结束标记。
* @return 返回读到的字符串,如果流结束仍没读到endFlag则返回null。
* @throws IOException 读取请求数据流时出现异常。
*/
private String readString(byte[] endFlag) throws IOException {
List result = new LinkedList(); //保存读出的字节
int flagLen = endFlag.length;
int safeLen = BUFFER_SIZE - flagLen;
int end; //结束标记在缓冲区中的位置
for(;;){ //循环读
end = fillTo(endFlag); //申请读到结束标记
if (end<=-2) { //没读到结束标记输入流就已经结束
return null;
} else { //读到结束标记或缓冲区满
byte[] oldBuffer = buffer; //保存旧缓冲区
buffer = new byte[BUFFER_SIZE];//创建新缓冲区
result.add(oldBuffer);
if (end < 0) { //读到缓冲区满
System.arraycopy(oldBuffer,safeLen,buffer,0,flagLen);
dataLength=flagLen;
} else { //读到结束标记
int remainLength = dataLength-end-flagLen;
System.arraycopy(oldBuffer,end + flagLen,buffer,0,remainLength);
dataLength=remainLength;
break;
}
}
} //for(;;)结束
//将列表中的多个数组合并成单个数组,并转换成字符串
byte[] single = new byte[(result.size()-1)*(safeLen)+end];
int index = 0;
for (Iterator i = result.iterator(); i.hasNext();index++) {
byte[] item = (byte[]) i.next();
if (i.hasNext()) //不是最后一个
System.arraycopy(item,0,single,index*safeLen,safeLen);
else
System.arraycopy(item,0,single,index*safeLen,end);
}
//使用request的编码字符集转换 -- modify by 黎新朝
return new String(single ,this.charset); //转换成字符串
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -