在Android中从HttpURLConnection获取InputStream时出现了UnknownLengthHttpInputStream。

16

HttpURLConnection.getInputStream() 返回 UnknownLengthHttpInputStream,由于这个原因,文档解析抛出 SAX parser 异常。

以下是代码:

try{
    URL url = new URL(uri);
    HttpURLConnection connection =
    (HttpURLConnection) url.openConnection();
    connection.setRequestMethod("GET");
    connection.setRequestProperty("Accept", "application/xml");

    InputStream xml = connection.getInputStream();
    System.out.println(connection.getResponseCode());
    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
    DocumentBuilder db = dbf.newDocumentBuilder();
    Document doc = db.parse(connection.getInputStream());
    doc.getDocumentElement().normalize();

}catch(Exception e){
    e.printStackTrace();
}

有人知道UnknownLengthHttpInputStream的原因吗?我只在Android上遇到这个错误,但是在Java项目中这段代码运行得非常完美。

以下是LogCat中的异常信息:

08-08 11:07:40.490: W/System.err(1493): org.xml.sax.SAXParseException: Unexpected end of document
08-08 11:07:40.504: W/System.err(1493): at org.apache.harmony.xml.parsers.DocumentBuilderImpl.parse(DocumentBuilderImpl.java:129)
08-08 11:07:40.510: W/System.err(1493): at javax.xml.parsers.DocumentBuilder.parse(DocumentBuilder.java:107)
08-08 11:07:40.510: W/System.err(1493): at com.example.testws.MainActivity.onCreate(MainActivity.java:59)
08-08 11:07:40.520: W/System.err(1493): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047)
08-08 11:07:40.520: W/System.err(1493): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1611)
08-08 11:07:40.520: W/System.err(1493): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1663)
08-08 11:07:40.520: W/System.err(1493): at android.app.ActivityThread.access$1500(ActivityThread.java:117)
08-08 11:07:40.530: W/System.err(1493): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:931)

提前致谢。


1
附加logcat中的异常? - Jens
3
你会得到 UnknownLengthHttpInputStream 流,因为你的服务器没有提供 Content-Length 或使用分块传输编码。当服务器都没有提供时,必须通过关闭连接来信号化HTTP主体的结束 - 在这种情况下,它似乎在文档中途关闭连接。在执行此调用时,你是否有可能通过嗅探Web服务器流量(例如使用 WireShark)? - Jens
1
在为“xml”变量赋值后立即尝试打印它。它会在控制台上打印响应还是生成错误? - Chanchal Shelar
1
对于SOAP调用,请尝试使用KSoap2而不是HTTP调用。 - Chanchal Shelar
你确定需要这样做吗? connection.setRequestProperty("Accept", "application/xml"); - Siddharth Tyagi
显示剩余8条评论
6个回答

3

很可能是一个 Http 1.0 (旧服务器或配置错误) 服务器,或者未配置 keep-alive。在这种情况下,流的长度在服务器关闭连接时是已知的。尝试在请求头中指定 http1.1 和 keep-alive(一些谷歌搜索会有所帮助)。只有在服务器响应中指定了内容长度属性,您才会提前知道流的长度。

解决方法:将 http 流完全读入 ByteBufferStream(直到 read() 返回 -1)。然后将 ByteBufferInputStream 抛给您的库(现在长度已知)。


0

尝试像这样

if(connection.getResponseCode() ==HttpURLConnection.HTTP_OK){

  InputStream xml = connection.getInputStream();   
  ..........
}else{

//problem in URI/connection .....
}

0

使用此方法来处理响应,它会处理所有事情!

public static ResponseHandler<String> getResponseHandlerInstance(final Handler handler) {
      final ResponseHandler<String> responseHandler = new ResponseHandler<String>() {

         public String handleResponse(final HttpResponse response) {
            Message message = handler.obtainMessage();
            Bundle bundle = new Bundle();
            StatusLine status = response.getStatusLine();
            Log.d(CLASSTAG, " " + HTTPRequestHelper.CLASSTAG + " statusCode - " + status.getStatusCode());
            Log.d(CLASSTAG, " " + HTTPRequestHelper.CLASSTAG + " statusReasonPhrase - " + status.getReasonPhrase());
            HttpEntity entity = response.getEntity();
            String result = null;
            if (entity != null) {
               try {
                  result = HTTPRequestHelper.inputStreamToString(entity.getContent());
                  bundle.putString("RESPONSE", result);
                  message.setData(bundle);
                  handler.sendMessage(message);
               } catch (IOException e) {
                  Log.e(CLASSTAG, " " + HTTPRequestHelper.CLASSTAG, e);
                  bundle.putString("RESPONSE", "Error - " + e.getMessage());
                  message.setData(bundle);
                  handler.sendMessage(message);
               }
            } else {
               Log.w(CLASSTAG, " " + HTTPRequestHelper.CLASSTAG + " empty response entity, HTTP error occurred");
               bundle.putString("RESPONSE", "Error - " + response.getStatusLine().getReasonPhrase());
               message.setData(bundle);
               handler.sendMessage(message);
            }
            return result;
         }
      };
      return responseHandler;
   }

   private static String inputStreamToString(final InputStream stream) throws IOException {
      BufferedReader br = new BufferedReader(new InputStreamReader(stream));
      StringBuilder sb = new StringBuilder();
      String line = null;
      while ((line = br.readLine()) != null) {
         sb.append(line + "\n");
      }
      br.close();
      return sb.toString();
   }

0
你尝试过使用Apache库来实现吗?我建议如下操作:
try {
        HttpClient client = new DefaultHttpClient();  
        String getURL = "http://www.google.com";
        HttpGet get = new HttpGet(getURL);
        HttpResponse responseGet = client.execute(get);  
        HttpEntity resEntityGet = responseGet.getEntity();  
        if (resEntityGet != null) {  
                    //do something with the response
                    Log.i("GET RESPONSE",EntityUtils.toString(resEntityGet));
                }
} catch (Exception e) {
    e.printStackTrace();
}

然后获取HttpEntity本身的流。

InputStream st = entity.getContent();

更多示例请参见:http://www.softwarepassion.com/android-series-get-post-and-multipart-post-requests/


0

SAX 解析器 无法获取 InputStream 数据的长度时,问题就出现了。为了解决这个问题,将从 xml (InputStream) 中读取的内容保存到一个 String 变量中,并从该变量创建一个 InputStream,用于 DocumentBuilderparse 方法。要做到这一点,请按以下方式修改您的代码:

try {
            URL url = new URL(uri);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("GET");
            connection.setRequestProperty("Accept", "application/xml");
            InputStream xml = connection.getInputStream();

            DataInputStream dis = new DataInputStream(xml);
            StringBuilder sb = new StringBuilder();
            int asci = dis.read();
            while (asci > 0) {
                sb.append((char) asci);
                asci = dis.read();
            }
            String inputXML = sb.toString();            
            String predefinedXML = "<?xml version=\"1.0\" encoding=\"UTF-8\"?> <EmotionDB></EmotionDB>";
            InputStream inputStream = new ByteArrayInputStream(inputXML.getBytes());//use predefinedXML.getBytes() instead of inputXML.getBytes() for sample test


            System.out.println(connection.getResponseCode());
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            DocumentBuilder db = dbf.newDocumentBuilder();
            Document doc = db.parse(inputStream);
            doc.getDocumentElement().normalize();

        } catch (Exception e) {
            e.printStackTrace();
        }

这里的inputStream将具有正确的内容长度,因为它是由一个固定字节数的ByteArrayInputStream构建的。


-2

在服务中,您需要正确关闭输出流以避免此异常。如果您正在使用第三方库,则请确保已设置响应头。

Content-Type
Content-Length

如果您正在使用Java服务,您可以从方法中获取内容长度。
File.length()

2
HTTP协议规定,在特定情况下可以没有Content-Length头部的响应:http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.4 此外,您不能总是修改正在调用的服务,因为它并不总是属于您自己。 - Jérôme Radix
那么你对解决方案有什么建议? - Siddharth Tyagi
说实话,我认为这是由于修复请求类型的原因。通过connection.setRequestProperty("Accept", "application/xml");您可能不知道URL的响应情况。 - Siddharth Tyagi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接