7.21
最近有人问我怎么进行文件上传,然后我就想到平常我们后台接受的文件要么就用各种文件上传接受jar包直接操作,要么就用struts2直接接收成File(话说File并不能指向内存中的文件内容,那这个File到底存在哪里是个问题)。所以我就想看看文件上传的原理,然后自己去实现下文件接收。
一、http文件报文的格式
-----------------------------92901418813201
Content-Disposition: form-data; name="title"
asd
-----------------------------92901418813201
Content-Disposition: form-data; name="image"; filename="1320140315.JPG"
Content-Type: image/jpeg
文件
-----------------------------92901418813201--
这是我找到的某http报文,这是request获取的inputstream直接获得的报文,还有request中的头部有两个重要的信息:
Content-Type:multipart/form-data;boundary=—————————7d33a816d302b6
Content-Length: 424,
解释下 Content-Type中的boundary是当type是multipart时特有的,用来分割内容,据说是自动生成,并且报文内容中不会有和他一样的字符串。
Content-Length是刚刚request获得的inputstream的报文的长度,是总长度
然后报文中格式是:
--boundary+\r\n //注意http报文中换行是\r\n
文件头信息+\r\n(可能还有头信息需要换行)
\r\n
信息内容+\r\n
--boundary||如果是报文结尾的话就是--boundary--
所以根据这个报文格式进行处理,就可以获得文件了。
二、代码
我的截取方法很简单,现将报文接收到byte[]中,再转成String,按照boundary截取后再按格式截取,就获得文件,再转成byte[],
这里我被编码方式坑了一次,以前都没注意过编码方式的问题
package com.yc.servlet;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class Userservlet extends HttpServlet {
private static final long serialVersionUID = 1L;
String Path = "E:/";
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doPost(request, response);
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws IOException {
Map<String, byte[]> map = parse(request);
Set<String> filenamesset = map.keySet(); // 遍历获得所有文件名
for (String name : filenamesset) { // 对文件名遍历 进行存储
File tempfile = new File(Path + new Date().getTime() + name);
FileOutputStream out = new FileOutputStream(tempfile);
out.write(map.get(name)); // 读入到磁盘,
out.flush();
out.close();
}
}
/*
*
* map 的存储: 只存储文件 文件名就是键 文件byte[] 是值
这个方法过滤不是multipart的报文
*/
public Map<String, byte[]> parse(HttpServletRequest request)//
throws IOException {
int length = request.getContentLength();
String Type = request.getContentType();
String boundary = Type.substring(Type.indexOf("boundary") + 9,Type.length());//获得boundary
InputStream in = request.getInputStream(); // 获取输入流
if (Type.startsWith("multipart/form-data;")) { // 判断是多数据上传
return subbyte(in, boundary, length);
} else {
throw new RuntimeException("非多数据情况");
}
}
/*
* 这个方法进行截取
* */
public Map<String, byte[]> subbyte(InputStream in, String boundary, int length) throws IOException {
Map<String, byte[]> map = new HashMap<String, byte[]>();
byte[] buffer = new byte[length];
in.read(buffer);
String contentbuffer = new String(buffer, "iso8859-1");//为什么是iso8859-1那,这里我被坑了很久,下一篇写字符编码讲解为什么
System.out.println(contentbuffer);
String[] contents = contentbuffer.split("(" + "--" + boundary + "--"+ ")|(" + "--" + boundary + ")");//按照 boundary分割
for (String c : contents) {
if (c.indexOf("filename") != -1) {
String name = c.substring(c.indexOf("filename=") + 10,c.indexOf("\"", c.indexOf("filename=\"") + 10));
String file1 = c.substring(c.indexOf("\r\n\r\n") + 4);
file1 = file1.substring(0, file1.length() - 2);
byte[] file2 = file1.getBytes("iso8859-1");
map.put(name, file2);
}
}
return map;
}
}
这个处理方法并不好,我觉得应该还有某种更好的流操作,但是我流操作学的并不好,找不到一个可以进行截取的流,等我复习了流操作说不定可以改善下