1. 程式人生 > >Android網路開源庫-Retrofit(二) 檔案上傳、下載及進度監聽

Android網路開源庫-Retrofit(二) 檔案上傳、下載及進度監聽

1.寫在前面的話

首先說明,我還沒太搞懂retrofit,目前,這篇部落格只能給出這幾個內容。

  • 檔案上傳
  • 檔案下載
  • 檔案下載的進度監聽

還有這兩點沒弄好,

  • 多檔案一次上傳 (批量上傳)
  • 檔案上傳進度監聽

當前使用版本

compile 'com.squareup.retrofit2:retrofit:2.0.2'

2. 檔案上傳

2.1 api 介面編寫

public interface uploadfileApi {
    @Multipart
    @POST("/fileabout.php")
    Call<String> upload(@Part
("fileName") String des, @Part("file\"; filename=\"1.txt") RequestBody file); }
  • @Part(“fileDes”) String des 可以加一些描述資訊(可以不加)
  • @Part(“file\”; filename=\”1.txt”) 格式不變,只需將1.text 對應的替換為你想在伺服器生成的檔名稱
  • 如果想傳多個檔案,多次請求,當然,也可以像表單一樣(還沒弄好)

當然,上面這種辦法的靈活性差了點,我們可以選擇下面這種寫法

public interface
uploadfileApi {
@Multipart @POST("/fileabout.php") Call<String> upload_2(@PartMap Map<String,RequestBody> params); }

2.2 上傳檔案

第一種api介面對應的程式碼

Retrofit retrofit= new Retrofit.Builder()
                        .addConverterFactory(GsonConverterFactory.create())
                        .baseUrl
("http://192.168.56.1") .build(); uploadfileApi service =retrofit.create(uploadfileApi.class); File file = new File(Environment.getExternalStorageDirectory() + "/" + "1.txt"); RequestBody requestBody = RequestBody.create(MediaType.parse("multipart/form-data"),file); Call<String> model = service.upload("this is txt",requestBody); model.enqueue(new Callback<String>() { @Override public void onResponse(Call<String> call, Response<String> response) { Log.e(TAG, "onResponse: " + response.body().toString() ); } @Override public void onFailure(Call<String> call, Throwable t) { } });
  • baseurl 為你的伺服器地址,(我這裡在區域網)
  • file 檔案為你手機中某個存在的檔案

第二中API,我們只需要將相應第一種中的引數用map存起來,不多說了。

2.3 伺服器接受檔案

伺服器接受檔案的程式碼就簡單多了,我這裡以php為例

```
<?php
    //var_dump($_POST);
    //var_dump($_FILES);
    $myfile = fopen("testfile.txt", "w");
    fwrite($myfile, $_FILES["file"]["tmp_name"]."\n"
        ."D:\WWW"."\\".$_FILES["file"]["name"]);
    move_uploaded_file($_FILES["file"]["tmp_name"], "D:\WWW"."\\".$_FILES["file"]["name"]);

上面這個程式碼就是將檔案的檔名寫入到textfile.txt檔案中,並且將檔案寫在當前d:\www\目錄下,檔名就是上傳的檔名。
結果如下如:
這裡寫圖片描述

3. 檔案下載及速度監聽

Retrofit並沒有給我們提供檔案下載進度的相關資訊,但是,我們還是可以從一些渠道知道如何監聽下載進度,在OKHTTP的官方demo裡面有一個Progress.java的檔案,從名字上就知道與進度有關。github地址

3.1 改造改造ResponseBody

okhttp3預設的responsebody是不能滿足我們的要求的,(不能知道進度的相關資訊),我們需要作出改造,首先需要個介面,監聽進度資訊。其次,好吧,我承認這是廢話,我們只需要把Progress.java中我們需要的拿出來就好。

3.1.1 interface
public interface ProgressListener {
    /**
     * @param progress     已經下載或上傳位元組數
     * @param total        總位元組數
     * @param done         是否完成
     */
    void onProgress(long progress, long total, boolean done);
}
3.1.2 ProgressResponseBody
public class ProgressResponseBody extends ResponseBody {

    private final ResponseBody responseBody;
    private final ProgressListener listener;
    private BufferedSource bufferedSource;

    public ProgressResponseBody(ResponseBody responseBody,ProgressListener listener){
        this.responseBody = responseBody;
        this.listener = listener;
    }
    @Override
    public MediaType contentType() {
        return responseBody.contentType();
    }

    @Override
    public long contentLength() {
        return responseBody.contentLength();
    }

    @Override
    public BufferedSource source() {
        if (null == bufferedSource){
            bufferedSource = Okio.buffer(source(responseBody.source()));
        }
        return bufferedSource;
    }

    private Source source(Source source) {
        return new ForwardingSource(source) {
            long totalBytesRead = 0L;

            @Override
            public long read(Buffer sink, long byteCount) throws IOException {
                long bytesRead = super.read(sink, byteCount);
                totalBytesRead += bytesRead != -1 ? bytesRead : 0;
                listener.onProgress(totalBytesRead, responseBody.contentLength(), bytesRead == -1);
                return bytesRead;
            }
        };
    }
}

恩,就是這些東西,別為我okio的相關知識,我也正在學呢。這個檔案就是ophttp3的官方demo裡面的東西。

3.2 使用自己的okhttpclient

我們需要通過OkHttpClient的攔截器去攔截Response,並將我們的ProgressReponseBody設定進去,這樣才能監聽進度。那麼,我們怎麼講client設定進去呢。通過觀察Retrofit的結構發現,Builder下面有client()方法可以設定,好,那麼我們通過Retrofit.Builder來建立(這樣我們可以配置了)。
這裡寫圖片描述
相關程式碼如下

Retrofit.Builder builder = new Retrofit.Builder()
                .baseUrl("http://192.168.56.1");
OkHttpClient client = new OkHttpClient.Builder()
                .addNetworkInterceptor(new Interceptor() {
                    @Override
                    public okhttp3.Response intercept(Chain chain) throws IOException {
                        okhttp3.Response orginalResponse = chain.proceed(chain.request());

                        return orginalResponse.newBuilder()
                                .body(new ProgressResponseBody(orginalResponse.body(), new ProgressListener() {
                                    @Override
                                    public void onProgress(long progress, long total, boolean done) {
                                        Log.e(TAG, Looper.myLooper()+"");
                                        Log.e(TAG, "onProgress: " + "total ---->" + total + "done ---->" + progress );
                                    }
                                }))
                                .build();
                    }
                })
                .build();
        DownLoadApi api = builder.client(client)
                .build().create(DownLoadApi.class);

* 注意進度的監聽發生在子執行緒中,要切記*

3.3 將response寫入到檔案裡

寫入的操作就簡單了,程式碼如下,沒什麼好說的。

Call<ResponseBody> call = api.getFile("image_text.png");
        call.enqueue(new Callback<ResponseBody>() {
            @Override
            public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
                try {
                    InputStream is = response.body().byteStream();
                    File file = new File(Environment.getExternalStorageDirectory(), "text_img.png");
                    FileOutputStream fos = new FileOutputStream(file);
                    BufferedInputStream bis = new BufferedInputStream(is);
                    byte[] buffer = new byte[1024];
                    int len;
                    while ((len = bis.read(buffer)) != -1) {
                        fos.write(buffer, 0, len);
                        fos.flush();
                    }
                    fos.close();
                    bis.close();
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                Log.e(TAG,"success");
            }

            @Override
            public void onFailure(Call<ResponseBody> call, Throwable t) {

            }
        });

* 注意image_text.png是我事先將這張圖片放入到相應路徑下面的,如圖,要確定能訪問到才行 *
這裡寫圖片描述

3.4 最後結果展示

這裡寫圖片描述

4. 總結

retrofit的功能強大,靈活性強,但是這就意味著使用起來稍微麻煩一點(至少我是這樣認為的),但是,retrofit依賴於okhttp,okhttp是有demo供我們學習的,so,學習demo去吧,連結地址