【问题标题】:How to download a file with Retrofit2?如何使用 Retrofit2 下载文件?
【发布时间】:2016-03-13 09:51:51
【问题描述】:

如何使用 Retrofit2 从我的 PHP 服务器下载 文件(图像/视频)?

我无法在网上找到任何有关如何进行操作的资源或教程;我发现this post 处理 SO 上的某个下载错误,但我不太清楚。谁能指出我正确的方向?

更新:

这是我的代码:

FileDownloadService.java

public interface FileDownloadService {
    @GET(Constants.UPLOADS_DIRECTORY + "/{filename}")
    @Streaming
    Call<ResponseBody> downloadRetrofit(@Path("filename") String fileName);
}

MainActivity.java(@Blackbelt的解决方案)

private void downloadFile(String filename) {
    FileDownloadService service = ServiceGenerator
            .createService(FileDownloadService.class, Constants.SERVER_IP_ADDRESS);
    Call<ResponseBody> call = service.downloadRetrofit("db90408a4bb1ee65d3e09d261494a49f.jpg");

    call.enqueue(new Callback<ResponseBody>() {
        @Override
        public void onResponse(final Response<ResponseBody> response, Retrofit retrofit) {
            try {
                InputStream is = response.body().byteStream();
                FileOutputStream fos = new FileOutputStream(
                        new File(Environment.getExternalStorageDirectory(), "image.jpg")
                );
                int read = 0;
                byte[] buffer = new byte[32768];
                while ((read = is.read(buffer)) > 0) {
                    fos.write(buffer, 0, read);
                }

                fos.close();
                is.close();
            } catch (Exception e) {
                Toast.makeText(MainActivity.this, "Exception: " + e.toString(), Toast.LENGTH_LONG).show();
            }
        }

        @Override
        public void onFailure(Throwable t) {
            Toast.makeText(MainActivity.this, "Failed to download file...", Toast.LENGTH_LONG).show();
        }
    });
}

当 USB 调试处于活动状态时,我得到一个 FileNotFoundException,而当没有启用时,我得到一个 NetworkOnMainThreadException。

MainActivity.java:(@Emanuel的解决方案)

private void downloadFile(String filename) {
    FileDownloadService service = ServiceGenerator
            .createService(FileDownloadService.class, Constants.SERVER_IP_ADDRESS);
    Call<ResponseBody> call = service.downloadRetrofit("db90408a4bb1ee65d3e09d261494a49f.jpg");

    call.enqueue(new Callback<ResponseBody>() {
        @Override
        public void onResponse(final Response<ResponseBody> response, Retrofit retrofit) {
            Log.i(TAG, "external storage = " + (Environment.getExternalStorageState() == null));
            Toast.makeText(MainActivity.this, "Downloading file... " + Environment.getExternalStorageDirectory(), Toast.LENGTH_LONG).show();

            File file = new File(Environment.getDataDirectory().toString() + "/aouf/image.jpg");
            try {
                file.createNewFile();
                Files.asByteSink(file).write(response.body().bytes());
            } catch (Exception e) {
                Toast.makeText(MainActivity.this,
                        "Exception: " + e.toString(),
                        Toast.LENGTH_LONG).show();
            }
        }

        @Override
        public void onFailure(Throwable t) {
            Toast.makeText(MainActivity.this, "Failed to download file...", Toast.LENGTH_LONG).show();
        }
    });
}

我收到 FileNotFoundException。

【问题讨论】:

  • 尝试找出产生NetworkOnMainThreadException的行调用并从另一个线程调用它。或者从另一个线程调用整个downloadFile(...) 方法:new Thread(new Runnable() { public void run() { downloadFile("filename"); } }).start();

标签: java php android download retrofit


【解决方案1】:

这是一个展示如何下载 Retrofit JAR 文件的小示例。您可以根据自己的需要进行调整。

这是界面:

import com.squareup.okhttp.ResponseBody;
import retrofit.Call;
import retrofit.http.GET;
import retrofit.http.Path;

interface RetrofitDownload {
    @GET("/maven2/com/squareup/retrofit/retrofit/2.0.0-beta2/{fileName}")
    Call<ResponseBody> downloadRetrofit(@Path("fileName") String fileName);
}

这是一个使用接口的Java类:

import com.google.common.io.Files;
import com.squareup.okhttp.ResponseBody;
import retrofit.Call;
import retrofit.Callback;
import retrofit.Response;
import retrofit.Retrofit;

import java.io.File;
import java.io.IOException;

public class Main {

    public static void main(String... args) {
        Retrofit retrofit = new Retrofit.Builder().
                baseUrl("http://repo1.maven.org").
                build();

        RetrofitDownload retrofitDownload = retrofit.create(RetrofitDownload.class);

        Call<ResponseBody> call = retrofitDownload.downloadRetrofit("retrofit-2.0.0-beta2.jar");

        call.enqueue(new Callback<ResponseBody>() {
            @Override
            public void onResponse(Response<ResponseBody> response, Retrofit retrofitParam) {
                File file = new File("retrofit-2.0.0-beta2.jar");
                try {
                    file.createNewFile();
                    Files.asByteSink(file).write(response.body().bytes());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            @Override
            public void onFailure(Throwable t) {
            }
        });
    }
}

【讨论】:

  • Files.asByteSink 已弃用,我应该改用什么?
  • Files.asByteSink 来自 Google Guava 库,并且未被弃用。我刚刚检查了 API 文档。您可以使用 FileOutputStream 并将目标文件传递给构造函数。然后通过其write (byte[] buffer, int byteOffset, int byteCount) 方法将字节数组写入输出流。使用 0 作为 byteOffset,使用数组长度作为 byteCount。
  • 糟糕,我下载了旧版本(12.0)。我会升级到新的(18.0),谢谢。
  • 与@Blackbelt 的解决方案相同的情况:当 USB 调试处于活动状态时出现 FileNotFoundException,在未启用时出现 NetworkOnMainThreadException。
【解决方案2】:

如果有人偶然发现此响应,这就是我将改造与 Rx 结合使用的方法。每个下载的文件都会被缓存,任何具有相同 url 的后续请求都将返回已经下载的文件。

为了使用它,只需订阅这个 observable 并传递你的 url。这会将您的文件保存在下载目录中,因此如果您的应用面向 API 23 或更高版本,请确保请求权限。

  public Observable<File> getFile(final String filepath) {
    URL url = null;
    try {
        url = new URL(filepath);
    } catch (MalformedURLException e) {
        e.printStackTrace();
    }
    final String name = url.getPath().substring(url.getPath().lastIndexOf("/") + 1);
    final File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), name);
    if (file.exists()) {
        return Observable.just(file);
    } else {
        return mRemoteService.getFile(filepath).flatMap(new Func1<Response<ResponseBody>, Observable<File>>() {
            @Override
            public Observable<File> call(final Response<ResponseBody> responseBodyResponse) {
                return Observable.create(new Observable.OnSubscribe<File>() {
                    @Override
                    public void call(Subscriber<? super File> subscriber) {
                        try {

                            final File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getAbsoluteFile(), name);

                            BufferedSink sink = Okio.buffer(Okio.sink(file));
                            sink.writeAll(responseBodyResponse.body().source());
                            sink.flush();
                            sink.close();
                            subscriber.onNext(file);
                            subscriber.onCompleted();
                            file.deleteOnExit();
                        } catch (IOException e) {
                            Timber.e("Save pdf failed with error %s", e.getMessage());
                            subscriber.onError(e);
                        }
                    }
                });
            }
        });
    }
}

改造部分调用

@Streaming
@GET
Observable<retrofit2.Response<ResponseBody>> getFile(@Url String fileUrl);

【讨论】:

    【解决方案3】:

    要下载一个文件,您可能需要响应的原始InputStream 并在sdcard 上写入内容。为此,您应该使用ResponseBody 作为T 作为您的返回类型Call&lt;ResponseBody&gt;。然后您将使用Retrofit 到enqueue 一个

    Callback<ResponseBody>
    

    当onResponse

    @Override
    public void onResponse(final Response<ResponseBody> response, Retrofit retrofit) {
    

    被调用,你可以用response.byteStream()检索InputStream,从中读取,然后将你读到的内容写在sdcard上(看看here)

    【讨论】:

    • 我按照你的方式进行,但我被卡住了:当 USB 调试处于活动状态时,我得到一个 FileNotFoundException,而当没有启用时,我得到一个 NetworkOnMainThreadException。但我看不出我在哪里搞砸了。还有一件事:下载只能保存到外部存储?如果是,为什么? (我会用我写的代码更新我的帖子)
    猜你喜欢
    • 2019-07-28
    • 1970-01-01
    • 2016-11-27
    • 1970-01-01
    • 2015-02-07
    • 2011-10-10
    • 2011-06-07
    • 2013-08-18
    • 2017-06-15
    相关资源
    最近更新 更多