Java 如何使用ApacheHttpClient流式传输响应体
我需要一个api来执行八位字节流,它没有长度。它只是一个实时数据流。我遇到的问题是,当我提出请求时,它似乎试图在将信息读入inputstream之前等待内容的结束,但是它没有看到内容的结束,并且没有使用NoHttpResponse异常进行计时。下面是我的代码的简化版本:Java 如何使用ApacheHttpClient流式传输响应体,java,httpclient,apache-httpclient-4.x,apache-commons-httpclient,Java,Httpclient,Apache Httpclient 4.x,Apache Commons Httpclient,我需要一个api来执行八位字节流,它没有长度。它只是一个实时数据流。我遇到的问题是,当我提出请求时,它似乎试图在将信息读入inputstream之前等待内容的结束,但是它没有看到内容的结束,并且没有使用NoHttpResponse异常进行计时。下面是我的代码的简化版本: private static HttpPost getPostRequest() { // Build uri URI uri = new URIBuilder() .setScheme(
private static HttpPost getPostRequest() {
// Build uri
URI uri = new URIBuilder()
.setScheme("https")
.setHost(entity.getStreamUrl())
.setPath("/")
.build();
// Create http http
HttpPost httpPost = new HttpPost(uri);
String nvpsStr = "";
Object myArray[] = nvps.toArray();
for(int i = 0; i < myArray.length; i ++) {
nvpsStr += myArray[i].toString();
if(i < myArray.length - 1) {
nvpsStr += "&";
}
}
// Build http payload
String request = nvpsStr + scv + streamRequest + "\n\n";
// Attach http data
httpPost.setEntity(new StringEntity(URLEncoder.encode(request,"UTF-8")));
return httpPost;
}
// Where client is simply
// private static final CloseableHttpClient client = HttpClients.createDefault();
private static runPostRequest (HttpPost request) {
CloseableHttpResponse response = client.execute(request);
try {
HttpEntity ent = response.getEntity();
InputStream is = ent.getContent();
DataInputStream dis = new DataInputStream(is);
// Only stream the first 200 bytes
for(int i = 0; i < 200; i++) {
System.out.println(( (char)dis.readByte()));
}
} finally {
response.close();
}
}
private静态HttpPost getPostRequest(){
//构建uri
URI=新的URIBuilder()
.setScheme(“https”)
.setHost(entity.getStreamUrl())
.setPath(“/”)
.build();
//创建http
HttpPost HttpPost=新的HttpPost(uri);
字符串nvpsStr=“”;
对象myArray[]=nvps.toArray();
for(int i=0;i
编辑2
因此,如果您不喜欢线程/可运行程序/处理程序,也不喜欢android AsyncTask,我会直接转到HttpUrlConnection(放弃使用Apache HttpClient的整个练习,因为基本上Google说HttpUrlConnection将支持流式响应,而且它确实有效!)
检测所有细节可能不像转储标题那样容易。但是对于一个正常的流式响应对象,我认为它应该可以正常工作。。。。有关HttpsUrlConnection代码示例,请参见编辑3
EndEdit2
不清楚使用了什么“流”协议(渐进下载或HTTP流),或者您实际上是如何管理客户端上的流式响应的
建议从连接中转储标头,以准确查看客户端和服务器的一致意见
我假设您脱离了UI线程(在AsyncTask或处理程序的回调部分);如果这不准确,您可能需要重构一点
假设HTTP流与Apache HttpClient 4.3.5+一起使用
如果响应的头中没有长度,则您在HTTP 1.1上执行“分块”响应,您必须读取缓冲区,直到获得“最后一个分块”或决定关闭流或连接:
服务器刚刚开始发送(流式传输),客户机应根据Apache关于生成实体内容的详细说明,使用缓冲区来处理从HTTP响应中获得的“输入流”。
我不记得30秒的套接字超时是否会抢占活动流?请记住,在Apache中,构建器中存在套接字超时和读取超时的单独设置。不希望套接字关闭,也不希望在服务器提供响应时等待可读流的可用字节超时
无论如何,客户端处理程序只需要通过检查读入缓冲区的内容来了解流是如何结束的
如果适当的协议是“continue”和“chunked”,那么客户端上的响应处理程序应该在流处理程序循环中,直到它看到来自流的最后一个块为止
应该为您提供在“最后一块”之前处理响应流所需的引用
我认为您应该了解如何使用一个缓冲实体,其中需要进行多次读取才能在响应中的“最后一块”结束。这是HttpURLConnection可能更容易的另一个原因
执行一个循环,处理缓冲读取,直到匹配“last chunk”的字节发出结束信号
然后根据Apache关于使用实体和可重用连接的详细说明关闭流或连接
编辑apache HttpClient中流式响应的代码
在处理程序的回调或异步任务中
request.execute();
...
processStreamingEntity(response.getEntity());
response.close();
//implement your own wrapper as mentioned in apache docs
private void processStreamingEntity(HttpEntity entity) throws IOException {
InputStreamHttpEntityHC4 bufHttpEntity = new InputStreamHttpEntityHC4(entity);
while not bufHttpEntity.LAST_CHUNK {
handleResponse(bufHttpEntity.readLine())
}
编辑3
HttpURLConnection版本,如果您这样做的话。(使用MessageHandler,但您可以就地使用字节,因为这是一个流式speach示例,文本中的单词将被发送回此处的UI)
试试RxSON:
它利用JsonPath wit RxJava从响应中读取JSON流块,并在响应完成之前将其解析为java对象
例如:
String serviceURL = "https://think.cs.vt.edu/corgis/datasets/json/airlines/airlines.json";
HttpRequest req = HttpRequest.newBuilder(URI.create(serviceURL)).GET().build();
RxSON rxson = new RxSON.Builder().build();
String jsonPath = "$[*].Airport.Name";
Flowable<String> airportStream = rxson.create(String.class, req, jsonPath);
airportStream
.doOnNext(it -> System.out.println("Received new item: " + it))
//Just for test
.toList()
.blockingGet();
String serviceURL=”https://think.cs.vt.edu/corgis/datasets/json/airlines/airlines.json";
HttpRequest req=HttpRequest.newBuilder(URI.create(serviceURL)).GET().build();
RxSON RxSON=new RxSON.Builder().build();
字符串jsonPath=“$[*].Airport.Name”;
可流动的airportStream=rxson.create(String.class、req、jsonPath);
机场流
.doOnNext(it->System.out.println(“收到的新项目:+it))
//只是为了测试
托利斯先生()
.blockingGet();
这对我来说很好。也许给我们看看你的服务器端。我无法控制服务器端代码。这只是我用来传输股票报价的第三方api。你能给我们举个例子吗?我尝试使用一个简单的servlet,它每隔几秒钟就传输几个字节,我在接收内容时没有遇到任何问题
private void openHttpsConnection(String urlStr, Handler mhandler) throws IOException {
HttpsURLConnection httpConn = null;
String line = null;
try {
URL url = new URL(urlStr);
URLConnection urlConn = url.openConnection();
if (!(urlConn instanceof HttpsURLConnection)) {
throw new IOException ("URL is not an Https URL");
}
httpConn = (HttpsURLConnection)urlConn;
httpConn.setAllowUserInteraction(false);
httpConn.setInstanceFollowRedirects(true);
httpConn.setRequestMethod("GET");
httpConn.setReadTimeout(50 * 1000);
BufferedReader is =
new BufferedReader(new InputStreamReader(httpConn.getInputStream()));
while ((line = is.readLine( )) != null) {
Message msg = Message.obtain();
msg.what=1;
msg.obj=line;
mhandler.sendMessage(msg);
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch( SocketTimeoutException e){
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
Message msg = Message.obtain();
msg.what=2;
BufferedInputStream in = new BufferedInputStream(httpConn.getErrorStream());
line =new String(readStream(in));
msg.obj=line;
mhandler.sendMessage(msg);
}
finally {httpConn.disconnect();}
}
String serviceURL = "https://think.cs.vt.edu/corgis/datasets/json/airlines/airlines.json";
HttpRequest req = HttpRequest.newBuilder(URI.create(serviceURL)).GET().build();
RxSON rxson = new RxSON.Builder().build();
String jsonPath = "$[*].Airport.Name";
Flowable<String> airportStream = rxson.create(String.class, req, jsonPath);
airportStream
.doOnNext(it -> System.out.println("Received new item: " + it))
//Just for test
.toList()
.blockingGet();