Android中HttpURLConnection網絡請求

HTTP網絡請求中的GET,POST,PUT,DELETE就對應着查,改,增,刪4個操做,而通常用的的是GET和POST。
1.GET請求的數據會附在URL以後(就是把數據放置在HTTP協議頭中),以?分割URL和傳輸數據,參數之間以&相連.若是數據是英文字母/數字,原樣發送,若是是空格,轉換爲+,若是是中文/其餘字符,則直接把字符串用BASE64加密。
GET方式提交的數據受瀏覽器及服務器對URL長度的限制。
可使用GET方式提交數據流,獲取數據流或下載文件。java

2.POST把提交的數據放置在是HTTP包的包體中,必需要到FORM(表單)。
POST方式提交的數據受服務器對錶單處理能力的限制。
能夠用POST方式提交數據流或文件,獲取數據流。json

 

這裏介紹的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();
    }
   }
  }
 }
}
相關文章
相關標籤/搜索