[英]How to stream response body with apache HttpClient
我需要一個 api 來執行沒有長度的八位字節流。 它只是一個實時數據流。 我遇到的問題是,當我提出請求時,它似乎在將信息讀入輸入流之前嘗試等待內容結束,但是它沒有看到內容結束和 NoHttpResponse 異常超時。 下面是我的代碼的簡化版本:
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();
}
}
編輯 2
因此,如果您對線程/可運行程序/處理程序不滿意並且對 android AsyncTask 不滿意,我會直接轉到 HttpUrlConnection(使用 Apache HttpClient 放棄整個練習,因為基本上谷歌說 HttpUrlConnection 將支持流式響應,並且確實如此工作!)
檢測所有細節(如轉儲標頭)可能並不容易。 但是對於普通的流式響應對象,我認為它應該可以正常工作......請參閱 HttpsUrlConnection 代碼示例的編輯 3
結束編輯2
從問題中不清楚正在使用什么“流”協議(漸進式下載或 HTTP 流)或您如何實際管理客戶端上的流響應。
建議從連接中轉儲標頭以查看客戶端和服務器到底同意什么?
我假設您已關閉 UI 線程(在 AsyncTask 中或在 Handler 的回調部分); 如果這不准確,您可能需要稍微重構一下。
假設 HTTP 流與 Apache HttpClient 4.3.5+ 一起使用
如果響應的標頭中沒有長度,那么您正在 HTTP 1.1 上執行“分塊”響應,您必須讀取緩沖區,直到獲得“最后一個塊”或決定關閉流或連接:
服務器剛剛開始發送(流式傳輸),客戶端應該根據 Apache生成實體內容的詳細說明使用緩沖區來處理它從 HTTP 響應中獲取的“輸入流” 。
我不記得 30 秒的套接字超時是否會搶占活動流? 請記住,在 Apache 中,構建器中存在單獨的套接字超時設置和讀取超時設置。 不希望套接字在您身上關閉,也不希望在服務器提供響應時超時等待可讀流的可用字節。
無論如何,客戶端處理程序只需要通過檢查讀入緩沖區的內容來了解流如何結束......
如果適當的協議是“繼續”和“分塊”,那么客戶端上的響應處理程序應該處於流處理程序循環中,直到它看到來自http 規范的 LAST-CHUNK。
response.getEntity().getContent()
應該為您提供處理響應流所需的參考,直到“最后一個塊”...
我認為您應該在這里閱讀有關如何使用緩沖實體的信息,其中需要不止一次讀取才能在響應中的“最后一個塊”處結束。 這是 HttpURLConnection 可能更容易的另一個原因......
執行一個循環處理緩沖讀取,直到匹配“last-chunk”的字節發出 END 信號。
然后根據有關使用實體和可重用連接的詳細 Apache 說明關閉流或連接。
Apache HttpClient 中流式響應的編輯代碼
在“處理程序的回調”中或在 asyncTask 中
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 但您可以就地使用字節,因為這是來自流式語音示例,並且文本中的單詞將被發送回 UI 此處)
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();}
}
嘗試 RxSON: https : //github.com/rxson/rxson它利用 JsonPath 和 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();
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.