使用Android的OkHttp包實(shí)現(xiàn)基于HTTP協(xié)議的文件上傳下載
OkHttp的HTTP連接基礎(chǔ)
雖然在使用 OkHttp 發(fā)送 HTTP 請(qǐng)求時(shí)只需要提供 URL 即可,OkHttp 在實(shí)現(xiàn)中需要綜合考慮 3 種不同的要素來(lái)確定與 HTTP 服務(wù)器之間實(shí)際建立的 HTTP 連接。這樣做的目的是為了達(dá)到最佳的性能。
首先第一個(gè)考慮的要素是 URL 本身。URL 給出了要訪問(wèn)的資源的路徑。比如 URL http://www.baidu.com 所對(duì)應(yīng)的是百度首頁(yè)的 HTTP 文檔。在 URL 中比較重要的部分是訪問(wèn)時(shí)使用的模式,即 HTTP 還是 HTTPS。這會(huì)確定 OkHttp 所建立的是明文的 HTTP 連接,還是加密的 HTTPS 連接。
第二個(gè)要素是 HTTP 服務(wù)器的地址,如 baidu.com。每個(gè)地址都有對(duì)應(yīng)的配置,包括端口號(hào),HTTPS 連接設(shè)置和網(wǎng)絡(luò)傳輸協(xié)議。同一個(gè)地址上的 URL 可以共享同一個(gè)底層 TCP 套接字連接。通過(guò)共享連接可以有顯著的性能提升。OkHttp 提供了一個(gè)連接池來(lái)復(fù)用連接。
第三個(gè)要素是連接 HTTP 服務(wù)器時(shí)使用的路由。路由包括具體連接的 IP 地址(通過(guò) DNS 查詢(xún)來(lái)發(fā)現(xiàn))和所使用的代理服務(wù)器。對(duì)于 HTTPS 連接還包括通訊協(xié)商時(shí)使用的 TLS 版本。對(duì)于同一個(gè)地址,可能有多個(gè)不同的路由。OkHttp 在遇到訪問(wèn)錯(cuò)誤時(shí)會(huì)自動(dòng)嘗試備選路由。
當(dāng)通過(guò) OkHttp 來(lái)請(qǐng)求某個(gè) URL 時(shí),OkHttp 首先從 URL 中得到地址信息,再?gòu)倪B接池中根據(jù)地址來(lái)獲取連接。如果在連接池中沒(méi)有找到連接,則選擇一個(gè)路由來(lái)嘗試連接。嘗試連接需要通過(guò) DNS 查詢(xún)來(lái)得到服務(wù)器的 IP 地址,也會(huì)用到代理服務(wù)器和 TLS 版本等信息。當(dāng)實(shí)際的連接建立之后,OkHttp 發(fā)送 HTTP 請(qǐng)求并獲取響應(yīng)。當(dāng)連接出現(xiàn)問(wèn)題時(shí),OkHttp 會(huì)自動(dòng)選擇另外的路由進(jìn)行嘗試。這使得 OkHttp 可以自動(dòng)處理可能出現(xiàn)的網(wǎng)絡(luò)問(wèn)題。當(dāng)成功獲取到 HTTP 請(qǐng)求的響應(yīng)之后,當(dāng)前的連接會(huì)被放回到連接池中,提供給后續(xù)的請(qǐng)求來(lái)復(fù)用。連接池會(huì)定期把閑置的連接關(guān)閉以釋放資源。
文件上傳和下載實(shí)例:
1.不帶參數(shù)上傳文件
/**
* 上傳文件
* @param actionUrl 接口地址
* @param filePath 本地文件地址
*/
public <T> void upLoadFile(String actionUrl, String filePath, final ReqCallBack<T> callBack) {
//補(bǔ)全請(qǐng)求地址
String requestUrl = String.format("%s/%s", BASE_URL, actionUrl);
//創(chuàng)建File
File file = new File(filePath);
//創(chuàng)建RequestBody
RequestBody body = RequestBody.create(MEDIA_OBJECT_STREAM, file);
//創(chuàng)建Request
final Request request = new Request.Builder().url(requestUrl).post(body).build();
final Call call = mOkHttpClient.newBuilder().writeTimeout(50, TimeUnit.SECONDS).build().newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.e(TAG, e.toString());
failedCallBack("上傳失敗", callBack);
}
@Override
public void onResponse(Call call, Response response) throws IOException {
if (response.isSuccessful()) {
String string = response.body().string();
Log.e(TAG, "response ----->" + string);
successCallBack((T) string, callBack);
} else {
failedCallBack("上傳失敗", callBack);
}
}
});
}
2.帶參數(shù)上傳文件
/**
*上傳文件
* @param actionUrl 接口地址
* @param paramsMap 參數(shù)
* @param callBack 回調(diào)
* @param <T>
*/
public <T>void upLoadFile(String actionUrl, HashMap<String, Object> paramsMap, final ReqCallBack<T> callBack) {
try {
//補(bǔ)全請(qǐng)求地址
String requestUrl = String.format("%s/%s", upload_head, actionUrl);
MultipartBody.Builder builder = new MultipartBody.Builder();
//設(shè)置類(lèi)型
builder.setType(MultipartBody.FORM);
//追加參數(shù)
for (String key : paramsMap.keySet()) {
Object object = paramsMap.get(key);
if (!(object instanceof File)) {
builder.addFormDataPart(key, object.toString());
} else {
File file = (File) object;
builder.addFormDataPart(key, file.getName(), RequestBody.create(null, file));
}
}
//創(chuàng)建RequestBody
RequestBody body = builder.build();
//創(chuàng)建Request
final Request request = new Request.Builder().url(requestUrl).post(body).build();
//單獨(dú)設(shè)置參數(shù) 比如讀取超時(shí)時(shí)間
final Call call = mOkHttpClient.newBuilder().writeTimeout(50, TimeUnit.SECONDS).build().newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.e(TAG, e.toString());
failedCallBack("上傳失敗", callBack);
}
@Override
public void onResponse(Call call, Response response) throws IOException {
if (response.isSuccessful()) {
String string = response.body().string();
Log.e(TAG, "response ----->" + string);
successCallBack((T) string, callBack);
} else {
failedCallBack("上傳失敗", callBack);
}
}
});
} catch (Exception e) {
Log.e(TAG, e.toString());
}
}
3.帶參數(shù)帶進(jìn)度上傳文件
/**
*上傳文件
* @param actionUrl 接口地址
* @param paramsMap 參數(shù)
* @param callBack 回調(diào)
* @param <T>
*/
public <T> void upLoadFile(String actionUrl, HashMap<String, Object> paramsMap, final ReqProgressCallBack<T> callBack) {
try {
//補(bǔ)全請(qǐng)求地址
String requestUrl = String.format("%s/%s", upload_head, actionUrl);
MultipartBody.Builder builder = new MultipartBody.Builder();
//設(shè)置類(lèi)型
builder.setType(MultipartBody.FORM);
//追加參數(shù)
for (String key : paramsMap.keySet()) {
Object object = paramsMap.get(key);
if (!(object instanceof File)) {
builder.addFormDataPart(key, object.toString());
} else {
File file = (File) object;
builder.addFormDataPart(key, file.getName(), createProgressRequestBody(MEDIA_OBJECT_STREAM, file, callBack));
}
}
//創(chuàng)建RequestBody
RequestBody body = builder.build();
//創(chuàng)建Request
final Request request = new Request.Builder().url(requestUrl).post(body).build();
final Call call = mOkHttpClient.newBuilder().writeTimeout(50, TimeUnit.SECONDS).build().newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.e(TAG, e.toString());
failedCallBack("上傳失敗", callBack);
}
@Override
public void onResponse(Call call, Response response) throws IOException {
if (response.isSuccessful()) {
String string = response.body().string();
Log.e(TAG, "response ----->" + string);
successCallBack((T) string, callBack);
} else {
failedCallBack("上傳失敗", callBack);
}
}
});
} catch (Exception e) {
Log.e(TAG, e.toString());
}
}
4.創(chuàng)建帶進(jìn)度RequestBody
/**
* 創(chuàng)建帶進(jìn)度的RequestBody
* @param contentType MediaType
* @param file 準(zhǔn)備上傳的文件
* @param callBack 回調(diào)
* @param <T>
* @return
*/
public <T> RequestBody createProgressRequestBody(final MediaType contentType, final File file, final ReqProgressCallBack<T> callBack) {
return new RequestBody() {
@Override
public MediaType contentType() {
return contentType;
}
@Override
public long contentLength() {
return file.length();
}
@Override
public void writeTo(BufferedSink sink) throws IOException {
Source source;
try {
source = Okio.source(file);
Buffer buf = new Buffer();
long remaining = contentLength();
long current = 0;
for (long readCount; (readCount = source.read(buf, 2048)) != -1; ) {
sink.write(buf, readCount);
current += readCount;
Log.e(TAG, "current------>" + current);
progressCallBack(remaining, current, callBack);
}
} catch (Exception e) {
e.printStackTrace();
}
}
};
}
5.不帶進(jìn)度文件下載
/**
* 下載文件
* @param fileUrl 文件url
* @param destFileDir 存儲(chǔ)目標(biāo)目錄
*/
public <T> void downLoadFile(String fileUrl, final String destFileDir, final ReqCallBack<T> callBack) {
final String fileName = MD5.encode(fileUrl);
final File file = new File(destFileDir, fileName);
if (file.exists()) {
successCallBack((T) file, callBack);
return;
}
final Request request = new Request.Builder().url(fileUrl).build();
final Call call = mOkHttpClient.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.e(TAG, e.toString());
failedCallBack("下載失敗", callBack);
}
@Override
public void onResponse(Call call, Response response) throws IOException {
InputStream is = null;
byte[] buf = new byte[2048];
int len = 0;
FileOutputStream fos = null;
try {
long total = response.body().contentLength();
Log.e(TAG, "total------>" + total);
long current = 0;
is = response.body().byteStream();
fos = new FileOutputStream(file);
while ((len = is.read(buf)) != -1) {
current += len;
fos.write(buf, 0, len);
Log.e(TAG, "current------>" + current);
}
fos.flush();
successCallBack((T) file, callBack);
} catch (IOException e) {
Log.e(TAG, e.toString());
failedCallBack("下載失敗", callBack);
} finally {
try {
if (is != null) {
is.close();
}
if (fos != null) {
fos.close();
}
} catch (IOException e) {
Log.e(TAG, e.toString());
}
}
}
});
}
6.帶進(jìn)度文件下載
/**
* 下載文件
* @param fileUrl 文件url
* @param destFileDir 存儲(chǔ)目標(biāo)目錄
*/
public <T> void downLoadFile(String fileUrl, final String destFileDir, final ReqProgressCallBack<T> callBack) {
final String fileName = MD5.encode(fileUrl);
final File file = new File(destFileDir, fileName);
if (file.exists()) {
successCallBack((T) file, callBack);
return;
}
final Request request = new Request.Builder().url(fileUrl).build();
final Call call = mOkHttpClient.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.e(TAG, e.toString());
failedCallBack("下載失敗", callBack);
}
@Override
public void onResponse(Call call, Response response) throws IOException {
InputStream is = null;
byte[] buf = new byte[2048];
int len = 0;
FileOutputStream fos = null;
try {
long total = response.body().contentLength();
Log.e(TAG, "total------>" + total);
long current = 0;
is = response.body().byteStream();
fos = new FileOutputStream(file);
while ((len = is.read(buf)) != -1) {
current += len;
fos.write(buf, 0, len);
Log.e(TAG, "current------>" + current);
progressCallBack(total, current, callBack);
}
fos.flush();
successCallBack((T) file, callBack);
} catch (IOException e) {
Log.e(TAG, e.toString());
failedCallBack("下載失敗", callBack);
} finally {
try {
if (is != null) {
is.close();
}
if (fos != null) {
fos.close();
}
} catch (IOException e) {
Log.e(TAG, e.toString());
}
}
}
});
}
7.接口ReqProgressCallBack.java實(shí)現(xiàn)
public interface ReqProgressCallBack<T> extends ReqCallBack<T>{
/**
* 響應(yīng)進(jìn)度更新
*/
void onProgress(long total, long current);
}
/**
* 統(tǒng)一處理進(jìn)度信息
* @param total 總計(jì)大小
* @param current 當(dāng)前進(jìn)度
* @param callBack
* @param <T>
*/
private <T> void progressCallBack(final long total, final long current, final ReqProgressCallBack<T> callBack) {
okHttpHandler.post(new Runnable() {
@Override
public void run() {
if (callBack != null) {
callBack.onProgress(total, current);
}
}
});
}
相關(guān)文章
Android讀取手機(jī)通訊錄聯(lián)系人到自己項(xiàng)目
這篇文章主要為大家詳細(xì)介紹了Android讀取手機(jī)通訊錄聯(lián)系人到自己項(xiàng)目,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-07-07
Android實(shí)現(xiàn)加載時(shí)提示“正在加載,請(qǐng)稍后”的方法
在現(xiàn)在的很多應(yīng)用中,當(dāng)在加載的時(shí)候,如果頁(yè)面動(dòng)態(tài)數(shù)據(jù)較多,會(huì)有很長(zhǎng)一段時(shí)間的空白頁(yè)面,如果加上這個(gè)頁(yè)面正在加載的提示,使得應(yīng)用更加人性化。這篇文章就給大家分享了在 Android實(shí)現(xiàn)加載時(shí)提示“正在加載,請(qǐng)稍后”的方法,有需要的朋友們可以參考借鑒。2016-10-10
android開(kāi)發(fā)教程之startActivityForResult使用方法
這篇文章主要介紹了android開(kāi)發(fā)教程之startActivityForResult使用方法,需要的朋友可以參考下2014-03-03
android輸入框內(nèi)容改變的監(jiān)聽(tīng)事件實(shí)例
下面小編就為大家分享一篇android輸入框內(nèi)容改變的監(jiān)聽(tīng)事件實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-02-02
Android自定義ImageView實(shí)現(xiàn)點(diǎn)擊兩張圖片切換效果
這篇文章主要為大家詳細(xì)介紹了Android自定義ImageView實(shí)現(xiàn)點(diǎn)擊兩張圖片切換效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-12-12
Android BroadcastReceiver廣播注冊(cè)方式總結(jié)
這篇文章主要介紹了Android BroadcastReceiver廣播注冊(cè)方式總結(jié)的相關(guān)資料,需要的朋友可以參考下2017-01-01
Android自定義實(shí)現(xiàn)淘寶下拉刷新效果
這篇文章主要為大家詳細(xì)介紹了Android自定義實(shí)現(xiàn)淘寶下拉刷新效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-12-12

