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 Mapparams){ 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 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(); } } } }}