import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import javax.servlet.ServletInputStream;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
*
* @author mark
*/
public class UploadFile {
private static Log log = LogFactory.getLog(UploadFile.class);
/**
* 上传文件组件,调用该方法的servlet在使用该方法前必须先调用request.setCharacterEncoding()方法,设置编码格式。该编码格式须与页面编码格式一致。
* @param sis 数据流
* @param encoding 编码方式。必须与jsp页面编码方式一样,否则会有乱码。
* @param length 数据流长度
* @param upLoadPath 文件保存路径
* @throws FileNotFoundException
* @throws IOException
*/
public static HashMap uploadFile(ServletInputStream sis, String encoding, int length, String upLoadPath) throws IOException {
HashMap paramMap = new HashMap();
boolean isFirst = true;
String boundary = null;//分界符
byte[] tmpBytes = new byte[4096];//tmpBytes用于存储每行读取到的字节。
int[] readBytesLength = new int[1];//数组readBytesLength中的元素i[0],用于保存readLine()方法中读取的实际字节数。
int readStreamlength = 0;//readStreamlength用于记录已经读取的流的长度。
String tmpString = null;
tmpString = readLine(tmpBytes, readBytesLength, sis, encoding);
readStreamlength = readStreamlength + readBytesLength[0];
while (readStreamlength < length) {
if (isFirst) {
boundary = tmpString;
isFirst = false;
}
if (tmpString.equals(boundary)) {
String contentDisposition = readLine(tmpBytes, readBytesLength, sis, encoding);
readStreamlength = readStreamlength + readBytesLength[0];
String contentType = readLine(tmpBytes, readBytesLength, sis, encoding);
readStreamlength = readStreamlength + readBytesLength[0];
//当时上传文件时content-Type不会是null
if (contentType != null && contentType.trim().length() != 0) {
String paramName = getPramName(contentDisposition);
String fileName = getFileName(getFilePath(contentDisposition));
paramMap.put(paramName, fileName);
//跳过空格行
readLine(tmpBytes, readBytesLength, sis, encoding);
readStreamlength = readStreamlength + readBytesLength[0];
/*
* 文件名不为空,则上传了文件。
*/
if (fileName != null && fileName.trim().length() != 0) {
fileName = upLoadPath + fileName;
//开始读取数据
byte[] cash = new byte[4096];
int flag = 0;
FileOutputStream fos = new FileOutputStream(fileName);
tmpString = readLine(tmpBytes, readBytesLength, sis, encoding);
readStreamlength = readStreamlength + readBytesLength[0];
/*
*分界符跟结束符虽然看上去只是结束符比分界符多了“--”,其实不是,
*分界符是“-----------------------------45931489520280”后面有2个看不见的回车换行符,即0D 0A
*而结束符是“-----------------------------45931489520280--”后面再跟2个看不见的回车换行符,即0D 0A
*
*/
while (tmpString.indexOf(boundary.substring(0, boundary.length() - 2)) == -1) {
for (int j = 0; j < readBytesLength[0]; j++) {
cash[j] = tmpBytes[j];
}
flag = readBytesLength[0];
tmpString = readLine(tmpBytes, readBytesLength, sis, encoding);
readStreamlength = readStreamlength + readBytesLength[0];
if (tmpString.indexOf(boundary.substring(0, boundary.length() - 2)) == -1) {
fos.write(cash, 0, flag);
fos.flush();
} else {
fos.write(cash, 0, flag - 2);
fos.flush();
}
}
fos.close();
} else {
//跳过空格行
readLine(tmpBytes, readBytesLength, sis, encoding);
readStreamlength = readStreamlength + readBytesLength[0];
//读取分界符或者结束符
tmpString = readLine(tmpBytes, readBytesLength, sis, encoding);
readStreamlength = readStreamlength + readBytesLength[0];
}
} //当不是长传文件时
else {
String paramName = getPramName(contentDisposition);
String value = readLine(tmpBytes, readBytesLength, sis, encoding);
//去掉回车换行符(最后两个字节)
byte[] valueByte=value.getBytes(encoding);
value =new String(valueByte, 0, valueByte.length-2, encoding);
readStreamlength = readStreamlength + readBytesLength[0];
paramMap.put(paramName, value);
tmpString = readLine(tmpBytes, readBytesLength, sis, encoding);
readStreamlength = readStreamlength + readBytesLength[0];
}
}
}
sis.close();
return paramMap;
}
/**
* 从流中读取一行数据。
* @param bytes 字节数组,用于保存从流中读取到的字节。
* @param index 一个整型数组,只有一个元素,即index[0],用于保存从流中实际读取的字节数。
* @param sis 数据流
* @param encoding 组建字符串时所用的编码
* @return 将读取到的字节经特定编码方式组成的字符串。
*/
private static String readLine(byte[] bytes, int[] index, ServletInputStream sis, String encoding) {
try {
index[0] = sis.readLine(bytes, 0, bytes.length);//readLine()方法把读取的内容保存到bytes数组的第0到第bytes.length处,返回值是实际读取的 字节数。
if (index[0] < 0) {
return null;
}
} catch (IOException e) {
log.error("read line ioexception");
return null;
}
if (encoding == null) {
return new String(bytes, 0, index[0]);
} else {
try {
return new String(bytes, 0, index[0], encoding);
} catch (UnsupportedEncodingException ex) {
log.error("Unsupported Encoding");
return null;
}
}
}
private static String getPramName(String contentDisposition) {
String s = contentDisposition.substring(contentDisposition.indexOf("name=\"") + 6);
s = s.substring(0, s.indexOf('\"'));
return s;
}
private static String getFilePath(String contentDisposition) {
String s = contentDisposition.substring(contentDisposition.indexOf("filename=\"") + 10);
s = s.substring(0, s.indexOf('\"'));
return s;
}
private static String getFileName(String filePath) {
String rtn = null;
if (filePath != null) {
int index = filePath.lastIndexOf("/");//根据name中包不包含/来判断浏览器的类型。
if (index != -1)//包含/,则此时可以判断文件由火狐浏览器上传
{
rtn = filePath.substring(index + 1);//获得文件名
} else//不包含/,可以判断文件由ie浏览器上传。
{
index = filePath.lastIndexOf("\\");
if (index != -1) {
rtn = filePath.substring(index + 1);//获得文件名
} else {
rtn = filePath;
}
}
}
return rtn;
}
}
|