日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

android使用HttpURLConnection/HttpClient实现带参数文件上传

發布時間:2025/3/15 编程问答 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 android使用HttpURLConnection/HttpClient实现带参数文件上传 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

本文參考自【http://blog.csdn.net/crazy__chen/article/details/47703781】

在Android 2.3及以上版本,使用的是HttpURLConnection,而在Android 2.2及以下版本,使用的是HttpClient
HttpClient
DefaultHttpClient和它的兄弟AndroidHttpClient都是HttpClient具體的實現類,它們都擁有眾多的API,而且實現比較穩定,bug數量也很少。
但同時也由于HttpClient的API數量過多,使得我們很難在不破壞兼容性的情況下對它進行升級和擴展,所以目前Android團隊在提升和優化HttpClient方面的工作態度并不積極。
HttpURLConnection
HttpURLConnection是一種多用途、輕量極的HTTP客戶端,使用它來進行HTTP操作可以適用于大多數的應用程序。雖然HttpURLConnection的API提供的比較簡單,但是同時這也使得我們可以更加容易地去使用和擴展它。
不過在Android 2.2版本之前,HttpURLConnection一直存在著一些令人厭煩的bug。比如說對一個可讀的InputStream調用close()方法時,就有可能會導致連接池失效了。那么我們通常的解決辦法就是直接禁用掉連接池的功能:
private void disableConnectionReuseIfNecessary() {
// 這是一個2.2版本之前的bug
if (Integer.parseInt(Build.VERSION.SDK) < Build.VERSION_CODES.FROYO) {
System.setProperty("http.keepAlive", "false");
}

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

在Android 2.3版本的時候,我們加入了更加透明化的響應壓縮。HttpURLConnection會自動在每個發出的請求中加入如下消息頭,并處理相應的返回結果:?
Accept-Encoding: gzip
配置你的Web服務器來支持對客戶端的響應進行壓縮的功能,從而可以在這一改進上獲取到最大的好處。如果在壓縮響應的時候出現了問題,這篇文檔會告訴你如何禁用掉這個功能。
但是如果啟動了響應壓縮的功能,HTTP響應頭里的Content-Length就會代表著壓縮后的長度,這時再使用getContentLength()方法來取出解壓后的數據就是錯誤的了。正確的做法應該是一直調用InputStream.read()方法來讀取響應數據,一直到出現-1為止。
我們在Android 2.3版本中還增加了一些HTTPS方面的改進,現在HttpsURLConnection會使用SNI(Server Name Indication)的方式進行連接,使得多個HTTPS主機可以共享同一個IP地址。除此之外,還增加了一些壓縮和會話的機制。如果連接失敗,它會自動去嘗試重新進行連接。這使得HttpsURLConnection可以在不破壞老版本兼容性的前提下,更加高效地連接最新的服務器。
在Android 4.0版本中,我們又添加了一些響應的緩存機制。當緩存被安裝后(調用HttpResponseCache的install()方法),所有的HTTP請求都會滿足以下三種情況:
所有的緩存響應都由本地存儲來提供。因為沒有必要去發起任務的網絡連接請求,所有的響應都可以立刻獲取到。
視情況而定的緩存響應必須要有服務器來進行更新檢查。比如說客戶端發起了一條類似于 “如果/foo.png這張圖片發生了改變,就將它發送給我” 這樣的請求,服務器需要將更新后的數據進行返回,或者返回一個304 Not Modified狀態。如果請求的內容沒有發生,客戶端就不會下載任何數據。
沒有緩存的響應都是由服務器直接提供的。這部分響應會在稍后存儲到響應緩存中。
由于這個功能是在4.0之后的版本才有的,通常我們就可以使用反射的方式來啟動響應緩存功能。下面的示例代碼展示了如何在Android 4.0及以后的版本中去啟用響應緩存的功能,同時還不會影響到之前的版本:

在當前Android 6.0 版本已經刪除了HttpClient相關API,寫相應程序時建議大家選擇HttpConnection.

文件上傳是常見功能,然而Android網上大多數的文件上傳都使用httpclient,而且需要添加一個httpmine-jar,其實HttpURLConnection也可以實現文件上傳,但是它在移動端有個弊端,就是不能上傳大文件,所以這次說的方式,只能上傳一些較小的文件。

文件上傳,并且帶上一些參數,這需要我們了解http請求的構造方式,也就是它的格式。

HttpURLConnection需要我們自己構造請求頭部,也就是我們要拼接出一個正確完整的請求。

下面來看一個典型的例子

[java]?view plaincopy
  • POST?/api/feed/?HTTP/1.1??
  • Accept-Encoding:?gzip??
  • Content-Length:?225873??
  • Content-Type:?multipart/form-data;?boundary=OCqxMF6-JxtxoMDHmoG5W5eY9MGRsTBp??
  • Host:?www.myhost.com??
  • Connection:?Keep-Alive??
  • ??
  • --OCqxMF6-JxtxoMDHmoG5W5eY9MGRsTBp??
  • Content-Disposition:?form-data;?name="param1"??
  • Content-Type:?text/plain;?charset=UTF-8??
  • Content-Transfer-Encoding:?8bit??
  • ??
  • 888??
  • --OCqxMF6-JxtxoMDHmoG5W5eY9MGRsTBp??
  • Content-Disposition:?form-data;?name="param2"??
  • Content-Type:?text/plain;?charset=UTF-8??
  • Content-Transfer-Encoding:?8bit??
  • ??
  • "nihao"??
  • --OCqxMF6-JxtxoMDHmoG5W5eY9MGRsTBp??
  • Content-Disposition:?form-data;?name="images";?filename="/storage/emulated/0/Camera/jdimage/1xh0e3yyfmpr2e35tdowbavrx.jpg"??
  • Content-Type:?application/octet-stream??
  • Content-Transfer-Encoding:?binary??
  • ??
  • 這里是圖片的二進制數據??
  • --OCqxMF6-JxtxoMDHmoG5W5eY9MGRsTBp--??

  • 上面的例子中,我們首先看 [java]?view plaincopy
  • POST?/api/feed/?HTTP/1.1??
  • Accept-Encoding:?gzip??
  • Content-Length:?225873??
  • Content-Type:?multipart/form-data;?boundary=OCqxMF6-JxtxoMDHmoG5W5eY9MGRsTBp??
  • Host:?www.myhost.com??
  • Connection:?Keep-Alive??
  • 第一行:為POST方式,要請求的子路徑為/api/feed/,例如我們的服務器地址為www.myhost.com,然后我們的這個請求的完整路徑就是www.myhost.com/api/feed/,最后說明了HTTP協議的版本號為1.1

    第二行:數據壓縮方式

    第三行:數據長度

    第四行:multipart/form-data;是指上傳的數據類型,這里是指文件形式。boundary是我們必須指定的一個分界符,不同參數之間要用這個分界符隔開。而OCqxMF6-JxtxoMDHmoG5W5eY9MGRsTBp就是具體的分界符,這個參數我們可以自己隨機生成的。

    第五行:主機地址

    第六行:持久連接,Keep-Alive功能避免了建立或者重新建立連接

    第七行:換行,這個換行是必須的,我們使用\r\n來進行換行


    然后就是參數內容部分了,先來看

    [java]?view plaincopy
  • --OCqxMF6-JxtxoMDHmoG5W5eY9MGRsTBp??
  • Content-Disposition:?form-data;?name="param1"??
  • Content-Type:?text/plain;?charset=UTF-8??
  • Content-Transfer-Encoding:?8bit??
  • ??
  • 888??
  • 我們把上面的看成一個整體

    第一行:我要先用分隔符來聲明一個參數的開始。注意,分隔符前面還加了兩橫“--”,這個也是必須加上的!

    第二行:name="param1",其實param1就是傳遞的參數的鍵值,例如在get方式中,我們這樣寫http://www.baidu.com?param1=888

    第三行:同樣是內容格式,不過這次是指定傳文本,所以是text/plain; ?另外,指定了編碼方式charset=UTF-8

    第四行:描述的是消息請求(request)和響應(response)所附帶的實體對象(entity)的傳輸形式,簡單文本數據我們設置為8bit,文件參數我們設置為binary就行

    第五行:換行,這個是必須的!

    第六行:參數值,例如http://www.baidu.com?param1=888,就是888


    OK,我們看下一個參數,也是同理

    [java]?view plaincopy
  • --OCqxMF6-JxtxoMDHmoG5W5eY9MGRsTBp??
  • Content-Disposition:?form-data;?name="param2"??
  • Content-Type:?text/plain;?charset=UTF-8??
  • Content-Transfer-Encoding:?8bit??
  • ??
  • "nihao"??

  • 然后下一個參數,就是文件了

    雖然指定的內容不一樣,但是格式是一樣的

    [java]?view plaincopy
  • --OCqxMF6-JxtxoMDHmoG5W5eY9MGRsTBp??
  • Content-Disposition:?form-data;?name="images";?filename="/storage/emulated/0/Camera/jdimage/1xh0e3yyfmpr2e35tdowbavrx.jpg"??
  • Content-Type:?application/octet-stream??
  • Content-Transfer-Encoding:?binary??
  • ??
  • 這里是圖片的二進制數據??
  • --OCqxMF6-JxtxoMDHmoG5W5eY9MGRsTBp--??

  • OK,大家仔細看上面的格式,不能出一點差錯,因為格式不對,就上傳不了了。


    接下來,我們直接看我寫的一個帶參數文件上傳工具類

    [java]?view plaincopy
  • /**?
  • ?*?Created?by?kaiyi.cky?on?2015/8/16.?
  • ?*/??
  • public?class?FileUploader?{??
  • ????private?static?final?String?TAG?=?"uploadFile";??
  • ????private?static?final?int?TIME_OUT?=?10*10000000;?//超時時間??
  • ????private?static?final?String?CHARSET?=?"utf-8";?//設置編碼??
  • ????private?static?final?String?PREFIX?=?"--";??
  • ????private?static?final?String?LINE_END?=?"\r\n";??
  • ??
  • ????public?static?void?upload(String?host,File?file,Map<String,String>?params,FileUploadListener?listener){??
  • ????????String?BOUNDARY?=?UUID.randomUUID().toString();?//邊界標識?隨機生成?String?PREFIX?=?"--"?,?LINE_END?=?"\r\n";??
  • ????????String?CONTENT_TYPE?=?"multipart/form-data";?//內容類型??
  • ????????try?{??
  • ????????????URL?url?=?new?URL(host);??
  • ????????????HttpURLConnection?conn?=?(HttpURLConnection)?url.openConnection();??
  • ????????????conn.setReadTimeout(TIME_OUT);??
  • ????????????conn.setConnectTimeout(TIME_OUT);??
  • ????????????conn.setRequestMethod("POST");?//請求方式??
  • ????????????conn.setRequestProperty("Charset",?CHARSET);//設置編碼??
  • ????????????conn.setRequestProperty("connection",?"keep-alive");??
  • ????????????conn.setRequestProperty("Content-Type",?CONTENT_TYPE?+?";boundary="?+?BOUNDARY);??
  • ????????????conn.setDoInput(true);?//允許輸入流??
  • ????????????conn.setDoOutput(true);?//允許輸出流??
  • ????????????conn.setUseCaches(false);?//不允許使用緩存??
  • ????????????if(file!=null)?{??
  • ????????????????/**?*?當文件不為空,把文件包裝并且上傳?*/??
  • ????????????????OutputStream?outputSteam=conn.getOutputStream();??
  • ????????????????DataOutputStream?dos?=?new?DataOutputStream(outputSteam);??
  • ????????????????StringBuffer?sb?=?new?StringBuffer();??
  • ????????????????sb.append(LINE_END);??
  • ????????????????if(params!=null){//根據格式,開始拼接文本參數??
  • ????????????????????for(Map.Entry<String,String>?entry:params.entrySet()){??????????????????????????
  • ????????????????????????sb.append(PREFIX).append(BOUNDARY).append(LINE_END);//分界符??
  • ????????????????????????sb.append("Content-Disposition:?form-data;?name=\""?+?entry.getKey()?+?"\""?+?LINE_END);??
  • ????????????????????????sb.append("Content-Type:?text/plain;?charset="?+?CHARSET?+?LINE_END);??
  • ????????????????????????sb.append("Content-Transfer-Encoding:?8bit"?+?LINE_END);??
  • ????????????????????????sb.append(LINE_END);??
  • ????????????????????????sb.append(entry.getValue());??
  • ????????????????????????sb.append(LINE_END);//換行!??
  • ????????????????????}??
  • ????????????????}??
  • ????????????????sb.append(PREFIX);//開始拼接文件參數??
  • ????????????????sb.append(BOUNDARY);?sb.append(LINE_END);??
  • ????????????????/**?
  • ?????????????????*?這里重點注意:?
  • ?????????????????*?name里面的值為服務器端需要key?只有這個key?才可以得到對應的文件?
  • ?????????????????*?filename是文件的名字,包含后綴名的?比如:abc.png?
  • ?????????????????*/??
  • ????????????????sb.append("Content-Disposition:?form-data;?name=\"img\";?filename=\""+file.getName()+"\""+LINE_END);??
  • ????????????????sb.append("Content-Type:?application/octet-stream;?charset="+CHARSET+LINE_END);??
  • ????????????????sb.append(LINE_END);??
  • ????????????????//寫入文件數據??
  • ????????????????dos.write(sb.toString().getBytes());??
  • ????????????????InputStream?is?=?new?FileInputStream(file);??
  • ????????????????byte[]?bytes?=?new?byte[1024];??
  • ????????????????long?totalbytes?=?file.length();??
  • ????????????????long?curbytes?=?0;??
  • ????????????????Log.i("cky","total="+totalbytes);??
  • ????????????????int?len?=?0;??
  • ????????????????while((len=is.read(bytes))!=-1){??
  • ????????????????????curbytes?+=?len;??
  • ????????????????????dos.write(bytes,?0,?len);??
  • ????????????????????listener.onProgress(curbytes,1.0d*curbytes/totalbytes);??
  • ????????????????}??
  • ????????????????is.close();??
  • ????????????????dos.write(LINE_END.getBytes());\\一定還有換行??
  • ????????????????byte[]?end_data?=?(PREFIX+BOUNDARY+PREFIX+LINE_END).getBytes();??
  • ????????????????dos.write(end_data);??
  • ????????????????dos.flush();??
  • ????????????????/**?
  • ?????????????????*?獲取響應碼?200=成功?
  • ?????????????????*?當響應成功,獲取響應的流?
  • ?????????????????*/??
  • ????????????????int?code?=?conn.getResponseCode();??
  • ????????????????sb.setLength(0);??
  • ????????????????BufferedReader?br?=?new?BufferedReader(new?InputStreamReader(conn.getInputStream()));??
  • ????????????????String?line;??
  • ????????????????while((line=br.readLine())!=null){??
  • ????????????????????sb.append(line);??
  • ????????????????}??
  • ????????????????listener.onFinish(code,sb.toString(),conn.getHeaderFields());??
  • ????????????}??
  • ????????}?catch?(MalformedURLException?e)?{??
  • ????????????e.printStackTrace();??
  • ????????}?catch?(IOException?e)?{??
  • ????????????e.printStackTrace();??
  • ????????}??
  • ????}??
  • ??
  • ????public?interface?FileUploadListener{??
  • ????????public?void?onProgress(long?pro,double?precent);??
  • ????????public?void?onFinish(int?code,String?res,Map<String,List<String>>?headers);??
  • ????}??
  • }??

  • 使用方式是這樣的:

    [java]?view plaincopy
  • public?class?MainActivity?extends?FragmentActivity?{??
  • ?????
  • ????File?sdDir;??
  • ????@Override??
  • ????protected?void?onCreate(Bundle?savedInstanceState)?{??
  • ????????super.onCreate(savedInstanceState);??
  • ????????setContentView(R.layout.activity_main);??
  • ??
  • ????????sdDir?=?null;??
  • ????????boolean?sdCardExist?=?Environment.getExternalStorageState()??
  • ????????????????.equals(Environment.MEDIA_MOUNTED);???//判斷sd卡是否存在??
  • ????????if(sdCardExist)?{??
  • ????????????sdDir?=?Environment.getExternalStorageDirectory();//獲取跟目錄??
  • ????????}??
  • ????????final?HashMap<String,String>?map?=?new?HashMap<String,String>();??
  • ????????map.put("aa","bb");??
  • ????????new?Thread(){??
  • ????????????@Override??
  • ????????????public?void?run()?{??
  • ????????????????FileUploader.upload("上傳地址",?new?File(sdDir.getPath()?+?"/文件名"),?map,?new?FileUploader.FileUploadListener()?{??
  • ????????????????????@Override??
  • ????????????????????public?void?onProgress(long?pro,?double?precent)?{??
  • ????????????????????????Log.i("cky",?precent+"");??
  • ????????????????????}??
  • ??
  • ????????????????????@Override??
  • ????????????????????public?void?onFinish(int?code,?String?res,?Map<String,?List<String>>?headers)?{??
  • ????????????????????????Log.i("cky",?res);??
  • ????????????????????}??
  • ????????????????});??
  • ????????????}??
  • ????????}.start();??????????
  • ????}?????
  • } ?
  • 總結

    以上是生活随笔為你收集整理的android使用HttpURLConnection/HttpClient实现带参数文件上传的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。