您的位置:首页 > 理论基础 > 计算机网络

Android中HttpURLConnection网络请求

2015-02-03 00:00 435 查看
HTTP网络请求中的GET,POST,PUT,DELETE就对应着查,改,增,删4个操作,而一般用的的是GET和POST。
1.GET请求的数据会附在URL之后(就是把数据放置在HTTP协议头中),以?分割URL和传输数据,参数之间以&相连.如果数据是英文字母/数字,原样发送,如果是空格,转换为+,如果是中文/其他字符,则直接把字符串用BASE64加密。
GET方式提交的数据受浏览器及服务器对URL长度的限制。
可以使用GET方式提交数据流,获取数据流或下载文件。

2.POST把提交的数据放置在是HTTP包的包体中,必须要到FORM(表单)。
POST方式提交的数据受服务器对表单处理能力的限制。
可以用POST方式提交数据流或文件,获取数据流。

这里介绍的HttpURLConnection,是java的标准类, 在Android2.3版本及以后,HttpURLConnection是最佳的选择。它的API简单,体积较小。默认带gzip压缩和缓存机制可以有效地减少网络访问的流量,在提升速度和省电方面也起到了较大的作用。
* 注意:在Android2.2版本之前,HttpURLConnection一直存在着一些令人厌烦的bug,建议用HttpClient。 此外,不建议上传大文件,如果要上传大文件,建议用HttpClient。

HttpURLConnection在用POST方式发送URL请求时,URL请求参数的设定顺序是重中之重:
对connection对象的一切配置(那一堆set函数)都必须要在connect()之前完成,如果没有connect(),则必须在getInputStream()之前完成。
而getOutputStream().write()上传参数或上传文件必须在getInputStream()之前完成。

也就是说,getInputStream()以后,请求已经发送出去了,所有关于请求的配置都要在此前完成,该函数的返回流已经是服务器的反馈了。

public class HttpURLConnectionUtil {
public static final int READ_TIMEOUT = 30*1000;
public static final int CONNECT_TIMEOUT = 10*1000;
public static final int EXCEPTION_ERR = 888;
// 文件下载的状态
public static final int DOWNLOAD_FAIL = -1;  //下载失败
public static final int DOWNLOAD_PART = 0;  //下载了一部分
public static final int DOWNLOAD_COMPLETE = 1;  //完全下载

/**
* 采用POST方式提交数据流
* POST把提交的数据放置在是HTTP包的包体中,必须要到FORM(表单)。
* POST方式提交的数据受服务器对表单处理能力的限制。此方法不支持提交大文件
* @param handler
*             用消息发送机制返回HTTP Response结果
* @param urlStr
*             链接
* @param params
*             提交到服务器的参数
* */
public static void postWithHttpURLConnection(final Handler handler, final String urlStr,
final Map<String, String> params){
StringBuffer data = new StringBuffer();
Message msg = handler.obtainMessage();

HttpURLConnection conn = null;
InputStream is = null;
OutputStream os = null;
try {
/* 发送HTTP请求前的Request部分   */
URL url = new URL(urlStr);
conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(CONNECT_TIMEOUT);
conn.setReadTimeout(READ_TIMEOUT);
conn.setRequestMethod("POST");
// 设置是否向connection输出,因为这个是post请求,参数要放在http正文内,因此需要设为true
conn.setDoOutput(true);
// 要读取http的反馈,因此需要设为true
conn.setDoInput(true);
// 说明正文是urlencoded编码过的form参数,同时我们对参数要进行UTF_8 转码成byte字节流
conn.setRequestProperty("Content-Type","application/x-www-form-urlencoded");
// Post 请求不能使用缓存
conn.setUseCaches(false);
conn.setInstanceFollowRedirects(true);
// 正文的内容是通过outputStream流写入
if( params != null ) {
StringBuffer param = new StringBuffer();
int index = 0;
for (Map.Entry<String, String> entry : params.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
if( index > 0 ){
param.append("&");
}
param.append(key);
param.append("=");
param.append(value);
index += 1;
}
if(param.length() > 0) {
os = conn.getOutputStream();
// 这种格式数据value先进行UTF_8 转码成byte字节流
os.write(param.toString().getBytes("UTF_8"));
}
}
/* Request部分配置完成 */

/* 发送HTTP请求后的Response部分 */
int responseCode = conn.getResponseCode();
is = conn.getInputStream();
if ( is != null ) {
int readSize = 0;
byte[] buffer = new byte[1024];
while( ( readSize = is.read(buffer) ) != -1 ) {
//将流字节通过UTF-8转码成字符串
String str = new String(buffer,"UTF_8");
data.append(str);
}
}
String json = data.toString();
msg.what = responseCode;
msg.obj = json;
} catch (Exception e) {
msg.what = EXCEPTION_ERR;
msg.obj = e.getMessage();
} finally {
// 不管是否发生异常,都会调用的代码段
msg.sendToTarget();
if (os!=null){
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (is!=null){
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (conn!=null){
conn.disconnect();
}
}
}

/**
* 采用GET方式请求数据流
* GET提交的数据显示放置在HTTP协议头中,以?分割URL和传输数据,参数之间以&相连.
* GET提交的数据受浏览器及服务器对URL长度的限制,一般是1024字节。
* @param handler
*             用消息发送机制返回HTTP Response结果
* @param urlStr
*             链接
* */
public static void getWithHttpURLConnection(final Handler handler, final String urlStr){
StringBuffer data = new StringBuffer();
Message msg = handler.obtainMessage();
HttpURLConnection conn = null;
InputStream is = null;
try {
URL url = new URL(urlStr);
conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(CONNECT_TIMEOUT);
conn.setReadTimeout(READ_TIMEOUT);
conn.setRequestMethod("GET");

int responseCode = conn.getResponseCode();
is = conn.getInputStream();
if( is != null ) {
int readSize = 0;
byte[] buffer = new byte[1024];
is = conn.getInputStream();
while( ( readSize = is.read(buffer) ) != -1 ) {
//将流字节通过UTF-8转码成字符串
String str = new String(buffer,"UTF_8");
data.append(str);
}
}
msg.what = responseCode;
msg.obj = data.toString();
} catch (Exception e) {
msg.what = EXCEPTION_ERR;
msg.obj = e.getMessage();
} finally {
msg.sendToTarget();
if (conn!=null){
conn.disconnect();
}
if (is!=null){
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

/**
* 使用GET的方法下载大文件,可以支持断点续传。下载完的文件保存在用户定义的指定位置。
*
* @param handler
*             与主线程的交互,返回下载结果状态
* @param urlStr
*     下载链接
* @param filePath
*     下载文件保存路径
* @param fileName
*     下载文件的文件名
* */
public static void downloadFileWithHttpURLConnection(final Handler handler, final String urlStr,
final String filePath, final String fileName) {
DownloadFileInfo fileInfo = new DownloadFileInfo(urlStr,filePath,fileName);
int status = downloadWithHttpURLConnection(fileInfo);
if( status == DOWNLOAD_PART ) {
if( shouldRangeDownload(fileInfo) ) {
status = downloadRangeWithHttpURLConnection(fileInfo);
}
}
Message msg = handler.obtainMessage();
msg.what = status;
msg.obj = "";
handler.sendMessage(msg);
}

/**
* 判断是否可以断点续传
*
* @param DownloadFileInfo 保存下载信息:
*    mURL:下载链接
*    mFilePath:指定下载文件保存路径
*    mFileName:下载文件的文件名
*    mCompleteFileLength:待下载文件的Size
*    mCurrentFileLength:已经下载的文件的Size,用于断点续传
*    mEtag:服务器为某个文件生产的唯一标识值,每次文件有更新该值就会变化。
* */
private static boolean shouldRangeDownload(DownloadFileInfo fileInfo){
File file = new File(fileInfo.mFilePath+File.separator+fileInfo.mFileName);
if(!file.exists()){
// 原来下载的文件已经被删除了,重新下载
fileInfo.clearFileSizeRecord();
return false;
}

HttpURLConnection conn = null;
try {
URL url = new URL(fileInfo.mURL);
conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(CONNECT_TIMEOUT);
conn.setRequestMethod("GET");

String acceptRange= conn.getHeaderField("Accept-Ranges");
String etag = conn.getHeaderField("ETag");
if( acceptRange!=null && acceptRange.equalsIgnoreCase("bytes") &&
fileInfo.mEtag!=null && etag!=null && etag.equalsIgnoreCase( fileInfo.mEtag )) {
return true;
} else {
// 1. 服务器不支持断点续传
// 2. ETag改变了
// 应该重新下载整个文件
return false;
}

} catch (Exception e) {
return false;
} finally {
if (conn!=null){
conn.disconnect();
}
}
}
/**
* 一次性全部下载
* 1. 不要设置ReadTimeOut,如果要设置,最好时间长一些,因为大文件的下载本来就耗时
* 2. 下载过程采用一边读输入流,一边写入文件的方法,节约内存
* 3. 如果只下载了一部分就被中断,但是服务器不支持断点续传的话,必须重新下载
*/
public static int downloadWithHttpURLConnection(DownloadFileInfo fileInfo) {
HttpURLConnection conn = null;
InputStream is = null;
RandomAccessFile savedfile = null;
try {
URL url = new URL(fileInfo.mURL);
conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(CONNECT_TIMEOUT);
conn.setRequestMethod("GET");
//保存文件的标示
String etag = conn.getHeaderField("ETag");
if(etag!=null){
fileInfo.mEtag = etag;
}

//新建需要写入的RandomAccessFile
File dir = new File(fileInfo.mFilePath);
if(!dir.exists()){
dir.mkdirs();
}
File file = new File(fileInfo.mFilePath+File.separator+fileInfo.mFileName);
if(!file.exists()){
file.createNewFile();
}
savedfile = new RandomAccessFile(file,"rwd");

int responseCode = conn.getResponseCode();
if ( responseCode == 200 ) {//用来表示请求成功.
int len = conn.getContentLength();
if( len <= 0 ) {
return DOWNLOAD_FAIL;
}
fileInfo.mCompleteFileLength = len;

int readSize = 0;
byte[] buffer = new byte[1024];//1k内存
is = conn.getInputStream();
while( ( readSize = is.read(buffer) ) != -1 ) {
// 循环从输入流中读1k数据,写入文件
savedfile.write(buffer, 0, readSize);
fileInfo.mCurrentFileLength += readSize;
}
} else {
return DOWNLOAD_FAIL;
}
int status = DOWNLOAD_FAIL;
if( fileInfo.mCurrentFileLength > 0 ) {
if( fileInfo.mCurrentFileLength == fileInfo.mCompleteFileLength ) {
status = DOWNLOAD_COMPLETE;
} else {
status = DOWNLOAD_PART;
}
}
return status;
} catch (Exception e) {
int status = DOWNLOAD_FAIL;
if( fileInfo.mCurrentFileLength > 0 ) {
status = DOWNLOAD_PART;
}
return status;
} finally {
if( conn != null ) {
conn.disconnect();
}
if( is != null ) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if( savedfile != null ) {
try {
savedfile.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

/**
* 断点续传下载
* 注意:断点续传成功的getResponseCode()是206,不是200
*/
public static int downloadRangeWithHttpURLConnection(DownloadFileInfo fileInfo) {
HttpURLConnection conn = null;
InputStream is = null;
RandomAccessFile savedfile = null;
try {
URL url = new URL(fileInfo.mURL);
conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(CONNECT_TIMEOUT);
conn.setRequestMethod("GET");

int startPos = fileInfo.mCurrentFileLength;//开始位置
conn.setRequestProperty("Range", "bytes=" + startPos + "-");//设置获取实体数据的范围
conn.connect();

//新建需要写入的RandomAccessFile
File file = new File(fileInfo.mFilePath+File.separator+fileInfo.mFileName);
savedfile = new RandomAccessFile(file,"rwd");
savedfile.seek(startPos);

int responseCode = conn.getResponseCode();
if ( responseCode == 206 ) {//断点续传成功的反馈值是206,不是200
int len = conn.getContentLength();
if( len <= 0 ) {
return DOWNLOAD_FAIL;
}

byte[] buffer = new byte[1024];
int readSize = 0;
is = conn.getInputStream();
while( (readSize=is.read(buffer)) != -1 ) {
savedfile.write(buffer, 0, readSize);
fileInfo.mCurrentFileLength += readSize;
}
} else {
return DOWNLOAD_FAIL;
}
int status = DOWNLOAD_FAIL;
if( fileInfo.mCurrentFileLength > 0 ) {
if( fileInfo.mCurrentFileLength == fileInfo.mCompleteFileLength ) {
status = DOWNLOAD_COMPLETE;
} else {
status = DOWNLOAD_PART;
}
}
return status;
} catch (Exception e) {
int status = DOWNLOAD_FAIL;
if( fileInfo.mCurrentFileLength > 0 ) {
status = DOWNLOAD_PART;
}
return status;
} finally {
if (conn!=null){
conn.disconnect();
}
if(is!=null){
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(savedfile!=null){
try {
savedfile.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: