英文:
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,它们可以完全加载。我不知道如何进行调试。
我尝试将 getFileFromUrl
与 https
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="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);
答案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.
{"data":{"foo":"bar"
Content-Length: 22
...is what the API should report.
{"data":{"foo":"bar"}}
答案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<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();
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论