最近,为了暑假的实训,在学习Servlet&JSP,其中遇到了文件上传的问题,百度一下,基本上都是说用1) commons-fileupload-1.2.2-bin.zip2) commons-io-2.3-bin.zip包,第三方的API。而且都是相互转载的。这些API都封装了底层的细节,对于如何工作的,我们根本就不知道,这不利于学习。
废话不多说了!!!在此,通过看书,了解了两种方法实现上传。
方法一:自己解析request.getInputStream()中的信息
直接看post发送数据的数据格式。截图获取:是由得到字节流(ISO-8895-1编码格式的),然后转成字符形式后的效果。
从截图可以看到多次出现了--------7dd17……Content-Disposition: form-data;name="**"
???……。对于出现多少次这样的形式,则说明了你在表单中有多少个input这样的标签或其他带数据的标签。其中name表明了标签中的name值,然后接着是它的value。对于<input type="file" />的则还有Content-Type的说明,然后接着是它的字节内容(也可以类比为value吧)。
那么如何实现文件的上传呢?那么我们需要做的是解析这大块“乱码”,从中分离出文件的字节内容,然后将这些字节写入文件中。步骤如下:
ENCODING = “ISO-8895-1”
(1)读取request中的所有数据,并保存在字节数组中
/**
* @param request
* @return
* @throws IOException
*/
private byte[] readBody(HttpServletRequest request) throws IOException{
int lenth = request.getContentLength();
DataInputStream in = new DataInputStream(request.getInputStream());
byte[] body = new byte[lenth];
int total = 0;
while(total < lenth) {
int b = in.read(body, total, lenth);
total += b;
}
in.close();
return body;
}
(2)转化为ISO-8895-1编码格式的字符串
String textBody = new String(body,"ISO-8859-1");
(3)分析textBody,获取文件名,是由数据的格式来进行截取
/**
* 获取文件名
* @param reqBody
* @return
*/
private String getFileName(String reqBody) {
String filename = reqBody.substring(
reqBody.indexOf("filename=\"") + 10);// 10就是filename="
filename = filename.substring(0, filename.indexOf("\n"));
filename = filename.substring(filename.indexOf("\\") + 1,//
filename.indexOf("\""));
try {
String s = new String(filename.getBytes(ENCODING),"utf-8");
filename =s;
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return filename;
}
(4)截取文件内容
private Position getFilePosition (HttpServletRequest request, String textBody) throws UnsupportedEncodingException {
// 取得文件区段边界信息
String contenttype = request.getContentType();
String boundarytext = contenttype.substring(
contenttype.lastIndexOf("=") + 1,
contenttype.length());
// 取得实际上传文件的起始与结束位置
int pos = textBody.indexOf("filename=\"");
pos = textBody.indexOf("\n", pos) + 1;
pos = textBody.indexOf("\n", pos) + 1;
pos = textBody.indexOf("\n", pos) + 1;
// 文件描述信息后就文件内容,直到为文件边界为止,从pos开始找边界
int boundaryLoc = textBody.indexOf(boundarytext, pos) -4;
int begin = ( (textBody.substring(0, pos)).getBytes(ENCODING) ).length;
int end = ((textBody.substring(begin, boundaryLoc)).getBytes(ENCODING)).length;
return new Position(begin, end);
}
(5)写文件
/**
* 将流写入文件中
* @param fileName
* @param body
* @param p
*/
private void writeTo(String fileName, byte[] body, Position p) {
fileName = PATH + fileName;//PATH="D:/workspace/WebServlet/WebServlet/source/";
FileOutputStream out = null;
try {
out = new FileOutputStream(fileName);
out.write(body, p.begin, p.end);
out.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
if(out != null)
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
class Postion{
int begin;
int end;
public Position(int begin, int end){
this.begin = begin;
this.end = end;
}
}
经过上面5步后就可以实现文件上传了。在此如果想获取某文本框中的数据,则要这样一步一步的分析,对于多文件上传,也是要这样一个个的进行分析。这样真的好麻烦啊。于是有方法二可以简化很多哦。
方法二:利用javax.servlet.http.Part
只有两步额,而且很简单。
(1)获得文件名
/**
* 获得文件名
* @param part
* @return
*/
private String getFileName(Part part) {
String header = part.getHeader("Content-Disposition");
String filename = header.substring(header.indexOf("filename=\"") + 10
, header.lastIndexOf("\""));
return filename;
}
(2)写入文件
/**
* 写入文件
* @param filename
* @param part
* @throws IOException
*/
private void writeTo(String filename, Part part) throws IOException {
InputStream in = part.getInputStream();
OutputStream out = new FileOutputStream(PATH + filename);//PATH同上面的
byte[] buffer = new byte[1024];
int len = -1;
while((len = in.read(buffer)) != -1) {
out.write(buffer, 0, len);
}
in.close();
out.close();
}
在此,你肯定也在关注着如何获取其他控件中的值(比如:文本框)。这个很简单。
要说明的是,对与表单中的每个数据控件、按钮(比如:submit)都会被封装成一个Part,对于label是不会的哦。
首先:获取Part集合request.getParts(),会返回一个collection集合
然后:进行遍历,
如果是文件,则利用上面两步进行,(上面两步其实是由一个public方法来调用的,而这个 方法的返回值为boolean类型,如果这个文件上传成功则返回true,否则false)。故,在每次上 传时,可以判断是否成功,然后可以进行一些说明。
如果是按钮的话,则continue
如果是其他数据(比如,一个文本框或选择框等),则利用request.getParameter(str)
也就是像平常一样获取值。
总结:
通过上面两种方法是很容易实现文件上传的,其中第一种方法可以说是最最细节的解析数据,是
很好的一种了解底层方法。当然这样也是很费劲的,很麻烦的。我在写的过程中,就遇到了各种问题,1、乱码,因为忘记文件名的转码了。2、字节数组越界,这真的就是细节了,一个字节的误差都可能会导致越界的问题。不管怎么样,第一种方式就是更具post数据传输的格式进行分析的。
第二种方法当然是很简单的。毕竟在Part类中已经封装好了这些。
对于想了解更多细节,写写第一种方式还是不错的哦;对于想实现那些上传等功能,还是直接用第二种方式吧