HTTP消息未正确加载(Android)

huangapple go评论72阅读模式
英文:

HTTP messages are not loaded correctly ( Android )

问题

我想使用 volley 加载 JSON,并使用 BufferedInputStream 下载图像。由于这些请求是发往局域网 IP 地址,使用非加密的 HTTP 端口,我启用了 android:usesCleartextTraffic="true"

我从 Volley 得到了以下错误:

com.android.volley.ParseError: org.json.JSONException: 在字符 20 处未终止的对象 { "data":{ "foo":"bar"

我使用以下方式使用 BufferedInputStream

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;
}

如果我在服务器上提供 Content-Length 参数,有时会得到以下错误:

W/System.err: java.net.ProtocolException: 流意外结束
        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)

如果没有 Content-Length 参数,某些文件仍未完全加载。没有抛出 IOException,但如果我尝试读取某些图像,则会出现以下错误:

<Corrupt JPEG data: 数据段过早结束> 来自输出消息

这些错误会随机发生。如果我使用浏览器打开这些 URL,它们可以完全加载。我不知道如何进行调试。

我尝试将 getFileFromUrlhttps URL 一起使用,然后一切正常工作。不幸的是,我在这个项目中无法使用 https,只能使用 http。


编辑

我创建了一个带有一个线程的执行器服务,然后在 Runnable 实例中提交了 getFileFromUrl 调用,但我仍然得到了错误:

Executors.newFixedThreadPool(1);
英文:

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=&quot;true&quot;

I get this error from Volley:

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

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:

&lt;Corrupt JPEG data: premature end of data segment&gt; 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);

答案1

得分: 2

似乎该API(或OkHttp客户端)由于某种原因截断了响应主体。

要么是OkHttp客户端的读取超时较短(可以将此值提高),要么是API未能及时响应(或未能完整地返回JSON响应)。当它在Web浏览器中总是能够正确响应时,确实应该是(Android)客户端的问题。一种处理方式可能是捕获该ProtocolException,删除中止的下载,然后重试下载文件。

但我认为Content-Length头的值可能只是错误的。
我的意思是,当不手动设置时,这个值会被正确计算。
客户端可能只会在接收到错误指令时表现异常。

Content-Length告诉客户端接收缓冲区应该有多大...
即使响应没有被截断,客户端也会拒绝多出来的部分。

例如:

API显然报告的是Content-Length: 20

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

API应该报告的是Content-Length: 22

{"data":{"foo":"bar"}}
英文:

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.<br/>
I mean, this value would be calculated correctly, when NOT setting it manually.<br/>
The client may only act abnormal upon erroneous instructions received.

Content-Length tells the client how large the receive-buffer has to be ...<br/>
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.

{&quot;data&quot;:{&quot;foo&quot;:&quot;bar&quot;

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

{&quot;data&quot;:{&quot;foo&quot;:&quot;bar&quot;}}

答案2

得分: 0

从你的代码中可以看出,你只是通过OpenConnection打开了原始的http连接。然而,我不确定这是否是加载图像的正确(和简单)方式。你可以使用其他任何处理内容长度和头信息的图像加载库。

  • 例如,可以使用Glide
Glide.with(context)
        .asBitmap()
        .load(url)
        .into(new CustomTarget<Bitmap>() {

            @Override
            public void onResourceReady(@NonNull Bitmap resource, @Nullable Transition<? super Bitmap> transition) {
                // 对位图进行任何你想做的操作
            }

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

            }
        });
  • 要保存图像,可以使用你上面的代码,或者:
public void write(String fileName, Bitmap bitmap) {
  try (FileOutputStream out = new FileOutputStream(fileName)) {
      bitmap.compress(Bitmap.CompressFormat.PNG, 100, out); // bitmap是你的Bitmap实例
      // PNG是一种无损格式,压缩因子(100)将被忽略
  } catch (IOException e) {
      e.printStackTrace();
  }
}
英文:

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&lt;Bitmap&gt;() {

                @Override
                public void onResourceReady(@NonNull Bitmap resource, @Nullable Transition&lt;? super Bitmap&gt; 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();
  }
}

huangapple
  • 本文由 发表于 2020年9月2日 04:11:25
  • 转载请务必保留本文链接:https://go.coder-hub.com/63694791.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定