Android 使用改造下载图像文件

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/24158321/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-20 07:51:06  来源:igfitidea点击:

Use retrofit to download image file

androidretrofit

提问by Denny Huang

I use Retrofit 1.6.0 on my Android project,

我在我的 Android 项目上使用 Retrofit 1.6.0,

the request url:

请求网址:

https://example.com/image/thumbs/filename/sample.png

https://example.com/image/thumbs/filename/sample.png

My interface like this:

我的界面是这样的:

public interface ImageService {
    @GET("/image/thumbs/filename/{filename}")
    @Streaming
    void getThumbs(
        @Path("filename") String filename,
        Callback<Response> callback
    );
}

HTTP request was success, but there some error occur

HTTP 请求成功,但出现一些错误

D/Retrofit(27613): ---> HTTP GET https://example.com/image/thumbs/filename/sample.png
D/Retrofit(27613): ---> END HTTP (no body)
D/Retrofit(27613): <--- HTTP 200 https://example.com/image/thumbs/filename/sample.png (451ms)
D/Retrofit(27613): : HTTP/1.1 200 OK
D/Retrofit(27613): Connection: Keep-Alive
D/Retrofit(27613): Content-Disposition: inline; filename="sample.png"
D/Retrofit(27613): Content-Type: image/png; charset=binary
D/Retrofit(27613): Date: Wed, 11 Jun 2014 06:02:31 GMT
D/Retrofit(27613): Keep-Alive: timeout=5, max=100
D/Retrofit(27613): OkHttp-Received-Millis: 1402466577134
D/Retrofit(27613): OkHttp-Response-Source: NETWORK 200
D/Retrofit(27613): OkHttp-Sent-Millis: 1402466577027
D/Retrofit(27613): Server: Apache/2.2.22 (Ubuntu)
D/Retrofit(27613): Transfer-Encoding: chunked
D/Retrofit(27613): X-Powered-By: PHP/5.4.28-1+deb.sury.org~precise+1
D/Retrofit(27613): ---- ERROR https://example.com/image/thumbs/filename/sample.png
D/Retrofit(27613): java.io.UnsupportedEncodingException: binary
D/Retrofit(27613):      at java.nio.charset.Charset.forNameUEE(Charset.java:322)
D/Retrofit(27613):      at java.lang.String.<init>(String.java:228)
D/Retrofit(27613):      at retrofit.RestAdapter.logAndReplaceResponse(RestAdapter.java:478)
D/Retrofit(27613):      at retrofit.RestAdapter.access0(RestAdapter.java:109)
D/Retrofit(27613):      at retrofit.RestAdapter$RestHandler.invokeRequest(RestAdapter.java:333)
D/Retrofit(27613):      at retrofit.RestAdapter$RestHandler.access0(RestAdapter.java:220)
D/Retrofit(27613):      at retrofit.RestAdapter$RestHandler.obtainResponse(RestAdapter.java:278)
D/Retrofit(27613):      at retrofit.CallbackRunnable.run(CallbackRunnable.java:42)
D/Retrofit(27613):      at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
D/Retrofit(27613):      at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
D/Retrofit(27613):      at retrofit.Platform$Android.run(Platform.java:142)
D/Retrofit(27613):      at java.lang.Thread.run(Thread.java:841)
D/Retrofit(27613): Caused by: java.nio.charset.UnsupportedCharsetException: binary
D/Retrofit(27613):      at java.nio.charset.Charset.forName(Charset.java:309)
D/Retrofit(27613):      at java.nio.charset.Charset.forNameUEE(Charset.java:320)
D/Retrofit(27613):      ... 11 more
D/Retrofit(27613): ---- END ERROR

How do I solve this problem?

我该如何解决这个问题?

回答by Jesse Wilson

The issue is the content-type header on the response includes a bogus charset:

问题是响应中的内容类型标头包含一个虚假字符集:

Content-Type: image/png; charset=binary

Retrofit sees this and infers that the response is text that it can log. You should report the problem to the server's administrator.

Retrofit 看到这一点并推断响应是它可以记录的文本。您应该将问题报告给服务器管理员。

If you report the issue to Retrofit's issue tracker on GitHub, we can probably recover from this problem rather than crashing.

如果您将问题报告给 GitHub 上的 Retrofit 问题跟踪器,我们可能可以从此问题中恢复而不是崩溃。

回答by Haojun Fan

I am playing with rxjava and retrofit these days, I have a quick demo here. Talk is cheap, show you my code directly, hope it helps.

这些天我正在玩 rxjava 并进行改造,我在这里有一个快速演示。话不多说,直接给你看我的代码,希望对你有帮助。

public interface ImageService {

    String ENDPOINT = "HTTP://REPLACE.ME";

    @GET
    @Streaming
    Observable<Bitmap> getThumbs(@Url String filepath);

    /********
     * Helper class that sets up a new services
     *******/
    class Instance {

        static ImageService instance;

        public static ImageService get() {
            if (instance == null)
                instance = newImageService();
            return instance;
        }

        public static ImageService newImageService() {
            Retrofit retrofit = new Retrofit.Builder()
                    .baseUrl(ImageService.ENDPOINT)
                    .addConverterFactory(BitmapConverterFactory.create())
                    .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                    .build();
            return retrofit.create(ImageService.class);
        }
    }
}

And I wrote my own BitmapConverterFactory to convert byte stream to bitmap:

我编写了自己的 BitmapConverterFactory 来将字节流转换为位图:

public final class BitmapConverterFactory extends Converter.Factory {

    public static BitmapConverterFactory create() {
        return new BitmapConverterFactory();
    }


    private BitmapConverterFactory() {
    }

    @Override
    public Converter<ResponseBody, Bitmap> responseBodyConverter(Type type, Annotation[] annotations,
                                                                 Retrofit retrofit) {
        if (type == Bitmap.class) {
            return new Converter<ResponseBody, Bitmap>(){

                @Override
                public Bitmap convert(ResponseBody value) throws IOException {
                    return BitmapFactory.decodeStream(value.byteStream());
                }
            };
        } else {
            return null;
        }
    }

    @Override
    public Converter<?, RequestBody> requestBodyConverter(Type type, Annotation[] annotations,
                                                          Retrofit retrofit) {
        return null;
    }
}

Gradle dependencies here:

Gradle 依赖项在这里:

final RETROFIT_VERSION = '2.0.0-beta3'
compile "com.squareup.retrofit2:retrofit:$RETROFIT_VERSION"
compile "com.squareup.retrofit2:adapter-rxjava:$RETROFIT_VERSION"

Cheers, vanvency

干杯,万文达

回答by Spark.Bao

Of course we usually use Picasso to load image, but sometimes we really need use Retrofit to load a special image (like fetch a captcha image), you need add some header for request, get some value from header of response (of course you can also use Picasso + OkHttp, but in a project you have already use Retrofit to handle most of net requests), so here introduce how to implement by Retrofit 2.0.0 (I have already implemented in my project).

当然我们通常使用 Picasso 来加载图片,但有时我们真的需要使用 Retrofit 来加载一个特殊的图片(比如获取验证码图片),你需要为请求添加一些头部,从响应头部中获取一些值(当然你可以也使用Picasso + OkHttp,但是在一个项目中你已经使用了Retrofit来处理大部分的网络请求),所以这里介绍一下如何通过Retrofit 2.0.0来实现(我已经在我的项目中实现了)。

The key point is that you need use okhttp3.ResponseBodyto receive response, else Retrofit will parse the response data as JSON, not binary data.

关键是你需要okhttp3.ResponseBody用来接收响应,否则 Retrofit 会将响应数据解析为 JSON,而不是二进制数据。

codes:

代码:

public interface Api {
    // don't need add 'Content-Type' header, it not works for Retrofit 2.0.0
    // @Headers({"Content-Type: image/png"})
    @GET
    Call<ResponseBody> fetchCaptcha(@Url String url);
}

Call<ResponseBody> call = api.fetchCaptcha(url);
call.enqueue(new Callback<ResponseBody>() {
        @Override
        public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
            if (response.isSuccessful()) {
                if (response.body() != null) {
                    // display the image data in a ImageView or save it
                    Bitmap bm = BitmapFactory.decodeStream(response.body().byteStream());
                    ivCaptcha.setImageBitmap(bm);
                } else {
                    // TODO
                }
            } else {
                // TODO
            }
        }

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

回答by avgx

Another way is to turn off full logging, so the retrofit.RestAdapter.logAndReplaceResponse will not try to consume the response body

另一种方法是关闭完整日志记录,因此改造.RestAdapter.logAndReplaceResponse 不会尝试使用响应正文