简体   繁体   中英

Getting UnknownLengthHttpInputStream while getting InputStream from HttpURLConnection in android

HttpURLConnection.getInputStream() gives UnknownLengthHttpInputStream and due to this Document parsing throws SAX parser exception.

Following is the code

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

Any one knows the reason for UnknownLengthHttpInputStream. I'm getting this error only in android and this code works perfectly in Java Project.

Following is the exception from 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)

Thanks in advance.

Its probably a Http 1.0 (old server or misconfiguration) server or no keep alive configured. In this case the length of the stream is known at the time the connection is closed from the server. Try specifying http1.1 and keep-alive in your request header (some googling will help on that). Only if the content length attribute is specified in the server response, you'll know the stream length in advance.

Workaround: read the http stream into a ByteBufferStream completely (until read() returns -1 ). Then throw the ByteBufferInputStream to your library (length is known now)

Have you tried to use the apache libraries for this? I would suggest the following:

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

and then get the stream for the HttpEntity itself. Smth like:

InputStream st = entity.getContent();

More examples here: http://www.softwarepassion.com/android-series-get-post-and-multipart-post-requests/

To handle response use this method it will take care of all !!

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

Here the problem comes when SAX parser could not get the length of InputStream data. To fix this problem, save the contents read from the xml ( InputStream ) in to a String variable and make an InputStream from the variable for parse method of DocumentBuilder . To do this modify your code as follows :

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

Here the inputStream will have a correct content length as it is built from a ByteArrayInputStream with a fixed number of bytes.

Once try it like this

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

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

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

You will need to close the output stream properly within the service to avoid this exception. If you are using a third party library then make sure that you have set the response header

Content-Type
Content-Length

If you are using a java service you can get the content-length from method

File.length()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM