Iter Ator
Iter Ator

Reputation: 9324

HTTP messages are not loaded correctly ( Android )

I would like to load json with volley and download images using BufferedInputStream. Since these requests are made to LAN ip adresses on the non encrypted http port, I enabled android:usesCleartextTraffic="true"

I get this error from Volley:

com.android.volley.ParseError: org.json.JSONException: Unterminated object at character 20 of {"data":{"foo":"bar"

I use BufferedInputStream like this:

private boolean getFileFromUrl(String url, String path) {
    try (BufferedInputStream in = new BufferedInputStream(new URL(url).openStream());
         FileOutputStream fileOutputStream = new FileOutputStream(path)) {
        byte dataBuffer[] = new byte[1024];
        int bytesRead;
        while ((bytesRead = in.read(dataBuffer, 0, 1024)) != -1) {
            fileOutputStream.write(dataBuffer, 0, bytesRead);
        }
        fileOutputStream.close();

        return true;
    } catch (IOException e) {
        e.printStackTrace();
    }

    return false;
}

If I provide a Content-Length parameter on the server, sometimes I get this error:

W/System.err: java.net.ProtocolException: unexpected end of stream
        at com.android.okhttp.internal.http.Http1xStream$FixedLengthSource.read(Http1xStream.java:398)
        at com.android.okhttp.okio.RealBufferedSource$1.read(RealBufferedSource.java:372)
        at java.io.BufferedInputStream.fill(BufferedInputStream.java:248)
W/System.err:     at java.io.BufferedInputStream.read1(BufferedInputStream.java:288)
W/System.err:     at java.io.BufferedInputStream.read(BufferedInputStream.java:347)

If I have no Content-Length parameter, some of the files are still not fully loaded. No IOException is thrown, but I get this error, if I try to read some of the images:

<Corrupt JPEG data: premature end of data segment> from output_message

These errors happen randomly. If I open these urls with my browser, it can load them perfectly. I have no idea how to debug it.

I tried my getFileFromUrl with a https url, and then everíthing worked fine. Unfortunately I am not able to use https in this project, only http.


Edit

I created an executor service with one thread and then submit the getFileFromUrl calls in Runnable instances, but I still get the errors:

Executors.newFixedThreadPool(1);

Upvotes: 1

Views: 191

Answers (2)

GensaGames
GensaGames

Reputation: 5788

From your code you are just opening raw http connection with OpenConnection. However, I'm not sure it's right(and simple) way to load image. You can use any other library for Image loading, which would take care about content length and headers.

  • For ex with Glide.
    Glide.with(context)
            .asBitmap()
            .load(url)
            .into(new CustomTarget<Bitmap>() {

                @Override
                public void onResourceReady(@NonNull Bitmap resource, @Nullable Transition<? super Bitmap> transition) {
                    // do whatever you want with bitmap
                }

                @Override
                public void onLoadCleared(@Nullable Drawable placeholder) {

                }
            });
  • To save just use your code above or.

public void write(String fileName, Bitmap bitmap) {
  try (FileOutputStream out = new FileOutputStream(filename)) {
      bmp.compress(Bitmap.CompressFormat.PNG, 100, out); // bmp is your Bitmap 
  instance
      // PNG is a lossless format, the compression factor (100) is ignored
  } catch (IOException e) {
      e.printStackTrace();
  }
}

Upvotes: 0

Martin Zeitler
Martin Zeitler

Reputation: 76807

As it seems, that API (or OkHttp client) truncates the response body for some reason.

Either the OkHttp client has a low read-timeout (this value could be raised) or the API does not respond in time (or as whole JSON response). When it always responds properly in a web-browser, it should indeed be a(n Android) client-side issue. One way to handle this might be to catch that ProtocolException, delete the aborted download and then retry to download the file again.

But I'd assume that the Content-Length header's value might simply be wrong.
I mean, this value would be calculated correctly, when NOT setting it manually.
The client may only act abnormal upon erroneous instructions received.

Content-Length tells the client how large the receive-buffer has to be ...
and even if the response is not truncated, the client will reject the excess.

For example:

Content-Length: 20 ...is what the API apparently reports.

{"data":{"foo":"bar"

Content-Length: 22 ...is what the API should report.

{"data":{"foo":"bar"}}

Upvotes: 2

Related Questions