HTTP协议
在做android开发中难免会对网络进行访问,android发展了9年其生态圈已经非常的强大,全球各地的开发者为移动开发贡献了很多开源库帮助我们可以轻而易举的进行网络访问,这些开源库的特点是简单好用,极大的方便了我们日常开发需要,但是我们在知其所然的同时也要知其所以然。下面我们来详细的了解一下基于POST的文件上传分析,并附有代码带进度条。每一句代码都附有注释,解释其作用。
HTTP协议特点:
- 支持C/S(客户/服务器)模式
- 简单快速:请求方法常有GET,POST,HEAD,每种方法规定了客户与服务器联系的类型不同
- 灵活:传输的类型由Content-Type加以标记,允许传输任意类型的数据对象
- 无连接:无连接就是限制每次连接只处理一个请求
- 无状态:/* 这个可不是LOL的老状态*/ 无状态是指协议对于事务处理没有记忆能力
HTTP请求报文
HTTP报文是面向文本的,报文中的每一个字段都是一些ASCII码串,各个字段的长度是不确定的。HTTP有两类报文:请求报文和响应报文。一个HTTP请求报文由请求行(request line)、请求头部(header)、空行和请求数据4个部分组成.
上面的图片和原理都是网上大众使用的,我觉得挺好的就拿来用了,主要是方便理解,辅助看一下就行。
1.请求行
请求行由请求方法、URL字段和HTTP协议的版本组成,格式如下:
Method Request-URL HTTP-Version CRLF
Method 表示请求方法;Request-URL 是统一资源标识符;HTTP-Version HTTP协议版本; CRLF 表示回车和换行。
POST HTTP/1.1
/**
*
* @param url 请求地址这个大家都懂的
* @param params 参数表单,采用的是map传入,请求的时候携带的参数在这里传入
* @param files 上传的文件
* @param ringProgressBar 进度条
* @param handler
* @return
* @throws IOException
*/
public static String post(String url, Map<String, String> params, File files, RingProgressBar ringProgressBar, Handler handler)
throws IOException {
String BOUNDARY = java.util.UUID.randomUUID().toString(); //UUID是指在一台机器上生成的数字,它保证对在同一时空中的所有机器都是唯一的
String PREFIX = "--", LINEND = "\r\n";
String MULTIPART_FROM_DATA = "multipart/form-data"; //它会将表单的数据处理为一条消息,以标签为单元,用分隔符分开。既可以上传键值对,也可以上传文件
String CHARSET = "UTF-8"; //编码格式
long totalLength = 0;
try {
// 取得文件大小 这里一会显示上传进度条会用到
totalLength = getFileSize(files);
Log.d("lfq", "### size: " + totalLength);
} catch (Exception e) {
e.printStackTrace();
}
URL uri = new URL(url);
HttpURLConnection conn = (HttpURLConnection) uri.openConnection(); //用于配置默认的参数并返回HttpURLConnection
conn.setReadTimeout(500000);//设置读取超时时长
conn.setConnectTimeout(500000); // 设置连接超时时长
conn.setDoInput(true);// 允许输入
conn.setDoOutput(true);// 允许输出
conn.setUseCaches(false); // 不允许使用缓存
conn.setAllowUserInteraction(false); //是否允许用户交互
conn.setChunkedStreamingMode(0);//分块编码,我这里不需要,因为要计算上传进度
conn.setRequestMethod("POST");//上传方式
conn.setRequestProperty("connection", "keep-alive"); //设置HttpURLConnection请求头里面的属性
conn.setRequestProperty("Charsert", "UTF-8");
conn.setRequestProperty("Content-Type", MULTIPART_FROM_DATA + ";boundary=" + BOUNDARY);
// 首先组拼文本类型的参数
StringBuilder sb = new StringBuilder();
for (Map.Entry<String, String> entry : params.entrySet()) { //表单的属性写入
sb.append(PREFIX);
sb.append(BOUNDARY);
sb.append(LINEND);
sb.append("Content-Disposition: form-data; name=\"" + entry.getKey() + "\"" + LINEND);
sb.append("Content-Type: text/plain; charset=" + CHARSET + LINEND);
sb.append("Content-Transfer-Encoding: 8bit" + LINEND);
sb.append(LINEND);
sb.append(entry.getValue());
sb.append(LINEND);
}
DataOutputStream outStream = new DataOutputStream(conn.getOutputStream());
outStream.write(sb.toString().getBytes());
handler.sendEmptyMessage(0X122);
// 发送文件数据
if (files != null)
{
StringBuilder sb1 = new StringBuilder();
sb1.append(PREFIX);
sb1.append(BOUNDARY);
sb1.append(LINEND);
sb1.append("Content-Disposition: form-data; name=\"uploadfile\"; filename=\""
+ files.getName() + "\"" + LINEND);
sb1.append("Content-Type: application/octet-stream; charset=" + CHARSET + LINEND);
sb1.append(LINEND);
outStream.write(sb1.toString().getBytes());
InputStream is = new FileInputStream(files);
byte[] buffer = new byte[1024];
int hasUpLoadCount = 0;
int len = 0;
while ((len = is.read(buffer)) != -1) {
outStream.write(buffer, 0, len);
hasUpLoadCount += len;
int total = (int) (hasUpLoadCount * 100 / totalLength ); //计算上传的进度
ringProgressBar.setProgress(total); //设置进度条
}
is.close();
outStream.write(LINEND.getBytes());
}
// 请求结束标志
byte[] end_data = (PREFIX + BOUNDARY + PREFIX + LINEND).getBytes();
outStream.write(end_data);
outStream.flush();
// 得到响应码
int res = conn.getResponseCode();
Log.d("lfq", "post: "+res);
InputStream in = conn.getInputStream();
StringBuilder sb2 = new StringBuilder();
if (res == 200) {
int ch;
while ((ch = in.read()) != -1) {
sb2.append((char) ch);
}
}
outStream.close();
conn.disconnect();
return sb2.toString();
}
public static long getFileSize(File file) throws Exception {
if (file == null) {
return 0;
}
long size = 0;
if (file.exists()) {
FileInputStream fis = null;
fis = new FileInputStream(file);
size = fis.available();
}
return size;
}
如果我们是使用网络框架的话上面的一些配置都给我写好了。在windows中可以采用Fiddler来进行网络数据捉包,其官方的下载地址为 https://www.telerik.com/download/fiddler.打开Fiddlder,用浏览器访问网络就可以了。