Retrofit2-如何從服務器下載文件

在這篇博客中霜医,將會講述使用Retrofit十分需要的一個功能:怎么去下載文件,下面會展示一些下載文件需要寫的代碼片段抑片,從小的 png 圖片到大的 zip文件照宝。

原文地址

Retrofit 2 — How to Download Files from Server

怎么指定一個Retrofit請求

如果你剛剛開始閱讀這篇文章并且以前沒有寫過任何關于Retrofit請求的代碼,可以先看一下前面翻譯的Retrofit系列的文章雹顺。對于已經(jīng)用過的人來說丹墨,下載文件的請求和其他的請求看起來是差不多的。

// option 1: a resource relative to your base URL
@GET("/resource/example.zip")
Call<ResponseBody> downloadFileWithFixedUrl();

// option 2: using a dynamic URL
@GET
Call<ResponseBody> downloadFileWithDynamicUrlSync(@Url String fileUrl);  

如果你想下載的文件資源是靜態(tài)的(資源一直在同一個地址)并且和你的base URL相關聯(lián)无拗,可以使用第一種方式带到。正如你所看到的昧碉,這看起來就像是Retrofit 2的一般的請求英染,值得注意的是,我們指定了 ResponseBody 來作為返回的類型被饿。你不應該使用任何其他的類型四康,否則Retrofit會嘗試去解析和映射轉換,在下載任務的時候這些操作都是沒有意義的狭握。
第二種方式是Retrofit 2新加的闪金,你現(xiàn)在可以輕松的通過一個動態(tài)的URL作為請求的參數(shù),這個特性在下載文件的時候是格外有用的,因為一般url都是根據(jù)參數(shù)哎垦,使用者囱嫩,和時間所決定的,你可以動態(tài)的去構建完整的請求URL漏设。如果你還沒有使用過動態(tài)URL,可以回頭去看我已經(jīng)翻譯的另一篇文章:Retrofit2-如何在請求時使用動態(tài)URL
你可以自己選擇使用哪一種方法昏兆,然后我們接著看下一步各淀。

怎么調用這個請求方法

當我們聲明了一個方法后,我們需要去調用它犬性,代碼如下:

FileDownloadService downloadService = ServiceGenerator.create(FileDownloadService.class);

Call<ResponseBody> call = downloadService.downloadFileWithDynamicUrlSync(fileUrl);

call.enqueue(new Callback<ResponseBody>() {  
    @Override
    public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
        if (response.isSuccess()) {
            Log.d(TAG, "server contacted and has file");

            boolean writtenToDisk = writeResponseBodyToDisk(response.body());

            Log.d(TAG, "file download was a success? " + writtenToDisk);
        } else {
            Log.d(TAG, "server contact failed");
        }
    }

    @Override
    public void onFailure(Call<ResponseBody> call, Throwable t) {
        Log.e(TAG, "error");
    }
});

如果你對 ServiceGenerator.create() 感到迷惑瞻离,可以回頭看我翻譯的系列第一篇文章 Retrofit-開始并創(chuàng)建一個Android客戶端,一旦我們創(chuàng)建了這個service乒裆,就可以像其他請求一樣來調用這個請求套利,當然這只是第一步,真正重要的功能是 writeResponseBodyToDisk() 將返回的 ResponseBoby寫到磁盤中鹤耍。

怎么保存文件

這個 writeResponseBodyToDisk() 方法拿到了 ResponseBody 對象日裙,然后從里面讀流并寫到磁盤中,這個代碼只是看起來比較復雜惰蜜。

private boolean writeResponseBodyToDisk(ResponseBody body) {  
    try {
        // todo change the file location/name according to your needs
        File futureStudioIconFile = new File(getExternalFilesDir(null) + File.separator + "Future Studio Icon.png");

        InputStream inputStream = null;
        OutputStream outputStream = null;

        try {
            byte[] fileReader = new byte[4096];

            long fileSize = body.contentLength();
            long fileSizeDownloaded = 0;

            inputStream = body.byteStream();
            outputStream = new FileOutputStream(futureStudioIconFile);

            while (true) {
                int read = inputStream.read(fileReader);

                if (read == -1) {
                    break;
                }

                outputStream.write(fileReader, 0, read);

                fileSizeDownloaded += read;

                Log.d(TAG, "file download: " + fileSizeDownloaded + " of " + fileSize);
            }

            outputStream.flush();

            return true;
        } catch (IOException e) {
            return false;
        } finally {
            if (inputStream != null) {
                inputStream.close();
            }

            if (outputStream != null) {
                outputStream.close();
            }
        }
    } catch (IOException e) {
        return false;
    }
}

大部分的代碼只是 Java I/O的模板代碼昂拂,你可能需要去自己改變第一行文件保存的地址和文件保存的名字。當你做完這些后抛猖,已經(jīng)算是準備好了使用Retrofit來下載文件格侯,但是,仍然還有一些事情沒有解決财著,比如一個主要的問題:默認情況下联四,Retrofit會將服務器的返回全部放到內存之中,這個操作在返回JSON或者XML時是OK的撑教,因為這些都比較小朝墩,但是大文件就非常容易導致 Out-of-Memory-Errors
如果你的應用需要下載一些大的文件伟姐,我們強烈建議閱讀下面這一段收苏。

謹防大文件:使用 @Streaming

如果你在下載一個大的文件,Retrofit默認會將整個文件移到內存中愤兵,為了避免這種情況鹿霸,我們需要為這種請求加一個特殊的注解:

@Streaming
@GET
Call<ResponseBody> downloadFileWithDynamicUrlAsync(@Url String fileUrl);  

加入這個 @Streaming 聲明后并不是將整個文件全部放入內存中,而是實時的返回字節(jié)碼秆乳。值得注意的是懦鼠,如果你在添加了 @Streaming聲明的情況下依然使用上面的方式來進行下載钻哩,Android就會拋出一個溢出 android.os.NetworkOnMainThreadException
所以肛冶,最后一步就是把請求包裝到一個另外的線程中街氢,比如使用 AsyncTask

final FileDownloadService downloadService =  
                ServiceGenerator.create(FileDownloadService.class);

new AsyncTask<Void, Long, Void>() {  
   @Override
   protected Void doInBackground(Void... voids) {
       Call<ResponseBody> call = downloadService.downloadFileWithDynamicUrlSync(fileUrl);
       call.enqueue(new Callback<ResponseBody>() {
           @Override
           public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
               if (response.isSuccess()) {
                   Log.d(TAG, "server contacted and has file");

                   boolean writtenToDisk = writeResponseBodyToDisk(response.body());

                   Log.d(TAG, "file download was a success? " + writtenToDisk);
               }
               else {
                   Log.d(TAG, "server contact failed");
               }
           }
       return null;
   }
}.execute(); 

如果你記住了一個 @Streaming 聲明和上面這一段,你就能用Retrofit來有效的下載大文件而不是出現(xiàn)內存溢出的問題睦袖。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末阳仔,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子扣泊,更是在濱河造成了極大的恐慌近范,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,997評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件延蟹,死亡現(xiàn)場離奇詭異评矩,居然都是意外死亡,警方通過查閱死者的電腦和手機阱飘,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,603評論 3 392
  • 文/潘曉璐 我一進店門斥杜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人沥匈,你說我怎么就攤上這事蔗喂。” “怎么了高帖?”我有些...
    開封第一講書人閱讀 163,359評論 0 353
  • 文/不壞的土叔 我叫張陵缰儿,是天一觀的道長。 經(jīng)常有香客問我散址,道長乖阵,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,309評論 1 292
  • 正文 為了忘掉前任预麸,我火速辦了婚禮瞪浸,結果婚禮上,老公的妹妹穿的比我還像新娘吏祸。我一直安慰自己对蒲,他們只是感情好,可當我...
    茶點故事閱讀 67,346評論 6 390
  • 文/花漫 我一把揭開白布贡翘。 她就那樣靜靜地躺著蹈矮,像睡著了一般。 火紅的嫁衣襯著肌膚如雪床估。 梳的紋絲不亂的頭發(fā)上含滴,一...
    開封第一講書人閱讀 51,258評論 1 300
  • 那天,我揣著相機與錄音丐巫,去河邊找鬼。 笑死,一個胖子當著我的面吹牛递胧,可吹牛的內容都是我干的碑韵。 我是一名探鬼主播,決...
    沈念sama閱讀 40,122評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼缎脾,長吁一口氣:“原來是場噩夢啊……” “哼祝闻!你這毒婦竟也來了?” 一聲冷哼從身側響起遗菠,我...
    開封第一講書人閱讀 38,970評論 0 275
  • 序言:老撾萬榮一對情侶失蹤联喘,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后辙纬,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體豁遭,經(jīng)...
    沈念sama閱讀 45,403評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,596評論 3 334
  • 正文 我和宋清朗相戀三年贺拣,在試婚紗的時候發(fā)現(xiàn)自己被綠了蓖谢。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,769評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡譬涡,死狀恐怖闪幽,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情涡匀,我是刑警寧澤盯腌,帶...
    沈念sama閱讀 35,464評論 5 344
  • 正文 年R本政府宣布,位于F島的核電站陨瘩,受9級特大地震影響腊嗡,放射性物質發(fā)生泄漏。R本人自食惡果不足惜拾酝,卻給世界環(huán)境...
    茶點故事閱讀 41,075評論 3 327
  • 文/蒙蒙 一燕少、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蒿囤,春花似錦客们、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,705評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至脸侥,卻和暖如春建邓,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背睁枕。 一陣腳步聲響...
    開封第一講書人閱讀 32,848評論 1 269
  • 我被黑心中介騙來泰國打工官边, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留沸手,地道東北人。 一個月前我還...
    沈念sama閱讀 47,831評論 2 370
  • 正文 我出身青樓注簿,卻偏偏與公主長得像契吉,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子诡渴,可洞房花燭夜當晚...
    茶點故事閱讀 44,678評論 2 354

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,095評論 25 707
  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統(tǒng)中一些常見模式的工具(例如配置管理捐晶,服務發(fā)現(xiàn),斷路器妄辩,智...
    卡卡羅2017閱讀 134,654評論 18 139
  • 原文鏈接: Retrofit 2 — How to Download Files from Server原文出自:...
    小鄧子閱讀 48,541評論 19 87
  • 1. Java基礎部分 基礎部分的順序:基本語法惑灵,類相關的語法,內部類的語法眼耀,繼承相關的語法英支,異常的語法,線程的語...
    子非魚_t_閱讀 31,625評論 18 399
  • 沿墻布置畔塔,任意布置潭辈,平面布線自由連接,弧形連接
    當陽英雄閱讀 165評論 2 0