8

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?

Denny Huang
  • 651
  • 2
  • 7
  • 12
  • maybe look into http://square.github.io/picasso/ which is a image downloading library also made by square. else you should look into TypedFile on retrofit http://square.github.io/retrofit/javadoc/index.html – Aegis Jun 11 '14 at 09:28
  • I have other APIs use retrofit, if it need to load to view mayebe I will use picasso,thanks for your suggestion! – Denny Huang Jun 12 '14 at 04:29

4 Answers4

10

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.

Jesse Wilson
  • 39,078
  • 8
  • 121
  • 128
5

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
        }
    });
chengsam
  • 7,315
  • 6
  • 30
  • 38
Spark.Bao
  • 5,573
  • 2
  • 31
  • 36
4

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

Haojun Fan
  • 41
  • 1
1

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

Anti Veeranna
  • 11,485
  • 4
  • 42
  • 63
avgx
  • 644
  • 5
  • 8