最近在做向服务器提交文件,本来用的
xutils
,使用起来挺简单,代码超不过
10
行,但是想深入了解一下原理,所以就自己用
HttpURLConnection
实现文件的上传。
无论是浏览器通过表单提交文件,还是
APP
通过
post
提交,其实原理都是一样的。
APP
只要仿照浏览器的表单提交数据的样式去提交文件,服务器就可以解析并处理文件。
那就首先来看一下浏览器提交的数据样式和请求头的信息。
HTML
核心代码:
<form method="post" action="http://192.168.218.163:8080/Test/FileUpLoad" enctype="multipart/form-data">
<input type="file" name="file" size="20" ><br/>
<input type="submit" name="submit" value="提交">
</form>
请求的头部信息:
host:192.168.218.163:8080
connection:keep-alive
content-length:432
cache-control:max-age=0
origin:null
upgrade-insecure-requests:1
user-agent:Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36
content-type:multipart/form-data; boundary=—-WebKitFormBoundarymYCoIxqCv49mE4Ok
accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
accept-encoding:gzip, deflate
accept-language:zh-CN,zh;q=0.8
cookie:__guid=158737658.212612550479137800.1501464417829.6194; monitor_count=15
头部信息的
content-type是重点,如果是上传文件,表单的mime类型必须是multipart/form-data,“boundary”是用来隔开表单中不同部分数据的,
它是由
– -开头,而不是boundary
数据开头,以
– -结尾。
服务器获取到的数据:
——WebKitFormBoundarynug1Tk0jD0SCH33m
Content-Disposition: form-data; name=”file”; filename=”nuuid.ini”
Content-Type: application/octet-stream
##
这是上传文件的数据
##
——WebKitFormBoundarynug1Tk0jD0SCH33m–
上传文件,
post
出去的数据是比较严格的。数据必须以
–boundary
开头
每一条数据独占一行,内容描述信息样式:
Content-Disposition: form-data; name=”file”; filename=”nuuid.ini”
Content-Type: application/octet-stream
然后空一个空行(
/r/n
),再是具体的文件数据。然后
– -boundary- –
结尾。
下面开始写
Java
代码:
String uuid = UUID.randomUUID().toString();
String BOUNDARY = uuid;
String NewLine = "\r\n";
String spec = "http://192.168.218.163:8080/Test/FileUpLoad";
File file = new File("G:\\mv\\哥德巴赫猜想.txt");
FileInputStream fis=null;
DataOutputStream bos =null;
DataInputStream bis=null;
URL url = new URL(spec);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
//打开输出
connection.setDoOutput(true);
//打开输入
connection.setDoInput(true);
//关闭缓存
connection.setUseCaches(false);
//读取超时
connection.setReadTimeout(50*1000);
//连接超时
connection.setConnectTimeout(5*1000);
//请求方式POST
connection.setRequestMethod("POST");
//设置请求头
// connection.setRequestProperty("Connection", "Keep-Alive");
//connection.addRequestProperty("user-agent","Mozilla/5.0 (Linux; U; Android 4.4.2; zh-cn; NX507J Build/KVT49L) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1");
//必须设置,数据类型,编码方式,分界线
connection.setRequestProperty("Content-Type", "multipart/form-data; charset=utf-8; boundary="+BOUNDARY);
//connection.setRequestProperty("accept-encoding","gzip");
connection.setChunkedStreamingMode(1024 * 50);
bos = new DataOutputStream(connection.getOutputStream());
if (file.exists()) {
fis = new FileInputStream(file);
byte[]buff = new byte[1024];
bis = new DataInputStream(fis);
int cnt=0;
//数据以--BOUNDARY开始
bos.write(("--"+BOUNDARY).getBytes());
//换行
bos.write(NewLine.getBytes());
//内容描述信息
content = "Content-Disposition: form-data; name=\""+filename+"\"; filename=\""+file.getName()+"\"";
bos.write(content.getBytes());
bos.write(NewLine.getBytes());
// bos.write("Content-Type: application/octet-stream".getBytes());
// bos.write(NewLine.getBytes());
// bos.write("Content-Transfer-Encoding: binary".getBytes());
// bos.write(NewLine.getBytes());
bos.write(NewLine.getBytes());
//空一行后,开始通过流传输文件数据
while((cnt=bis.read(buff))!=-1){
bos.write(buff,0,cnt);
}
bos.write(NewLine.getBytes());
//结束标志--BOUNDARY--
bos.write(("--"+BOUNDARY+"--").getBytes());
bos.write(NewLine.getBytes());
bos.flush();
}
//开始发送请求,获取请求码和请求结果
if (connection.getResponseCode()==HttpURLConnection.HTTP_OK) {
connection.getInputStream();
System.out.println("url="+connection.getURL());
BufferedReader read = new BufferedReader(new InputStreamReader(connection.getInputStream(), "utf-8"));
String dat=null;
while ((dat =read.readLine())!=null){
System.out.println(dat);
}
System.out.println("请求成功");;
}else {
System.err.println("请求失败"+connection.getResponseMessage()+"code="+connection.getResponseCode());
}
请求体的内容:
–32eeb4fb-7fe9-4fa3-a5bf-ec357c48a5d4
Content-Disposition: form-data; name=”file”
Content-Type: text/plain; charset=utf-8
文本文件
–32eeb4fb-7fe9-4fa3-a5bf-ec357c48a5d4
Content-Disposition: form-data; name=”哥德巴赫猜想.txt”; filename=”哥德巴赫猜想.txt”
##
上传文件的内容
(文本或二进制)
–32eeb4fb-7fe9-4fa3-a5bf-ec357c48a5d4–
注:
1.post
到服务器的数据格式要求严格,以
—
BOUNDARY
开头,紧跟着是内容描述信息(
Content-Disposition: form-data; name=”
filename
“; filename=” ”
)
,
然后空一行+内容正
文,
–BOUNDARY–
结尾。
2.
注释的代码,是不必须的参数。
3.
如果数据格式不按固定格式传输(描述信息和内容正文之间空一行),服务器在处理数据时会报
Stream ended unexpectedly
异常(服务器用的
Apache
的
FileUpload
组件)