无组件java上传文件初探
????? 最近闲来无事,突然对文件上传感兴趣起来,不由的想着自己不借助第三方包写个java文件上传的工具类,经过对http协议的一番研究,总算有点小成果。
????? 这是页面表单代码片段:
?在页面填写完数据后,提交表单,通过httpwatch我们可以看到如下数据:
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; }}