Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use retrofit to download image file

I use Retrofit 1.6.0 on my Android project,

the request url:

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

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.access$500(RestAdapter.java:109)
D/Retrofit(27613):      at retrofit.RestAdapter$RestHandler.invokeRequest(RestAdapter.java:333)
D/Retrofit(27613):      at retrofit.RestAdapter$RestHandler.access$100(RestAdapter.java:220)
D/Retrofit(27613):      at retrofit.RestAdapter$RestHandler$2.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$2$1.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?

like image 340
Denny Huang Avatar asked Jun 11 '14 08:06

Denny Huang


3 Answers

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.

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

like image 120
Jesse Wilson Avatar answered Oct 15 '22 01:10

Jesse Wilson


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).

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

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
        }
    });
like image 5
Spark.Bao Avatar answered Oct 15 '22 02:10

Spark.Bao


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.

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:

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:

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

Cheers, vanvency

like image 4
Haojun Fan Avatar answered Oct 15 '22 02:10

Haojun Fan