0

I want to download a zip file from the server in Android and for that I am using the below code. It also requires authorization so for that I am passing the username and password in the request of the URL connection. But I am always getting 401-Response code(UNAUTHORIZED).

Code :-

protected String doInBackground(final String... params) {
            int count;
            try {
                if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
                    /*Authenticator.setDefault(new Authenticator(){
                        protected PasswordAuthentication getPasswordAuthentication() {
                            return new PasswordAuthentication(Constants.USERNAME,Constants.PASSWORD.toCharArray());
                        }});*/

                    Log.i(TAG, "URL == " + params[0]);
                    final URL url = new URL(params[0]);
                    final HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                    final String auth = new String(Constants.USERNAME + ":" + Constants.PASSWORD);
                    connection.setRequestProperty("Authorization", "Basic " + Base64.encodeToString(auth.getBytes(), Base64.URL_SAFE));
                    //connection.addRequestProperty("Authorization", "Basic " + Base64.encodeToString(auth.getBytes(), Base64.DEFAULT));
                    connection.setUseCaches(false);
                    connection.setConnectTimeout(5000);
                    connection.setDoOutput(true);
                    connection.connect();
                    Log.i(TAG, "Response == " + connection.getResponseMessage());
                    Log.i(TAG, "Response Code == " + connection.getResponseCode());

                    // download the file
                    final InputStream input = new BufferedInputStream(connection.getInputStream());
                    // Path to the just created empty db

                    // this will be useful so that you can show a tipical 0-100% progress bar
                    lenghtOfFile = connection.getContentLength();
                    lengthOfFileInMB = lenghtOfFile / 1048576;
                    Log.i(TAG, "File Size in MB = " + lengthOfFileInMB);

                    outFileName = Environment.getExternalStorageDirectory() + File.separator + ZIP_NAME;
                    final File file = new File(outFileName);
                    if (!file.exists()) {
                        file.createNewFile();
                    }

                    // Output stream
                    final BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(file));
                    final byte data[] = new byte[lenghtOfFile];
                    long total = 0;

                    while ((count = input.read(data)) != -1) {
                        total += count;
                        publishProgress("" + total);
                        bufferedOutputStream.write(data, 0, count);
                    }
                    bufferedOutputStream.flush();
                    bufferedOutputStream.close();
                    input.close();
                } else {
                    Toast.makeText(DashboardActivity.this, "MEDIA IS NOT MOUNTED.", Toast.LENGTH_LONG).show();
                }
            } catch (final MalformedURLException e) {
                flag = false;
                Log.e(TAG, e.getMessage(), e);
            } catch (final FileNotFoundException e) {
                flag = false;
                Log.e(TAG, e.getMessage(), e);
            } catch (final IOException e) {
                flag = false;
                Log.e(TAG, e.getMessage(), e);
            } catch (final Exception e) {
                Log.e(TAG, e.getMessage(), e);
            }

            return null;
        }

I am having the web service in the same server and for getting JSON/XML response I used the DefaultHttpClient. That is working perfectly fine and I am getting the response code OK as well. I don't know why it is not authorizing at the time of URLConnection.

Here is the code of it.

String line = null;
        try {
            final URL urlObj = new URL(url);
            final HttpHost host = new HttpHost(urlObj.getHost(), urlObj.getPort(), urlObj.getProtocol());
            final HttpParams httpParameters = new BasicHttpParams();
            // Set the timeout in milliseconds until a connection is established.
            HttpConnectionParams.setConnectionTimeout(httpParameters, Constants.CONNECTION_TIME_OUT);
            // Set the default socket timeout (SO_TIMEOUT) 
            // in milliseconds which is the timeout for waiting for data.
            HttpConnectionParams.setSoTimeout(httpParameters, Constants.CONNECTION_TIME_OUT);

            final DefaultHttpClient httpClient = new DefaultHttpClient(httpParameters);
            final AuthScope scope = new AuthScope(urlObj.getHost(), urlObj.getPort());
            final UsernamePasswordCredentials creds = new UsernamePasswordCredentials(Constants.USERNAME, Constants.PASSWORD);
            final CredentialsProvider credentialProvider = new BasicCredentialsProvider();
            credentialProvider.setCredentials(scope, creds);
            final HttpContext credContext = new BasicHttpContext();
            credContext.setAttribute(ClientContext.CREDS_PROVIDER, credentialProvider);

            final HttpGet job = new HttpGet(url);
            job.addHeader("USER-AGENT", userAgentString);
            final HttpResponse httpResponse = httpClient.execute(host,job,credContext);

            final HttpEntity httpEntity = httpResponse.getEntity();

            try {
                line = EntityUtils.toString(httpEntity);
            } catch (final ParseException e) {
                line = "Error";
                Log.e("Parse Error", e.getMessage().toString());
            } catch (final IOException e) {
                line = "Error";
                Log.e("IOException Error", e.getMessage().toString());
            }
            final StatusLine status = httpResponse.getStatusLine();
            Log.d("Authentication Status = ", status.toString());
        } catch (final ClientProtocolException e1) {
            line = "Error";
            Log.e("ClientPrtocol Error", e1.getMessage().toString());
        } catch (final ConnectTimeoutException e1) {
            line = "ConnectionTimeOut";
            Log.e("Connection Error", e1.getMessage().toString());
        } catch (final IOException e1) {
            Log.e("IO Error", e1.getMessage().toString());
            line = "Error";
        }

I also tried to add the following code in URLConnection for authentication but that also not worked for me.

Authenticator.setDefault(new Authenticator() {
  protected PasswordAuthentication getPasswordAuthentication() {
     return new PasswordAuthentication(loginNameString, passwordString.toCharArray());
  }
});

Questions:-

1) Is it problem at server side or at android side?

2) Can I download the file using the defaulthttpclient code that I have? If yes any clue how to download it, because I think i can only get content from that way not the whole file.

Scorpion
  • 6,831
  • 16
  • 75
  • 123
  • I would say android side is more susceptible. why not using httpclient on Android also? If you want to stick with URLConnection, I would recommend using a HTTP snifer to dump the headers out, since you are already messing around with low level HTTP protocol details. – Jerry Tian Jan 23 '13 at 03:20
  • For downloading part, I thinks it is answered already here http://stackoverflow.com/questions/4757300/file-download-in-httpclient-using-java and here http://stackoverflow.com/questions/10960409/how-do-i-save-a-file-downloaded-with-httpclient-into-a-specific-folder – Jerry Tian Jan 23 '13 at 03:22
  • @JerryTian, 2nd link worked for me but partially. It started downloading file but always getting length if file (-1) and download never stops. – Scorpion Jan 25 '13 at 04:15
  • You can not always depend on content length, it is not required in HTTP protocol at all. Seems the code in second link is not generic enough. I will post the code below since I can not paste my code here. – Jerry Tian Jan 25 '13 at 04:52
  • But I want to know the length of the content as I am displaying that in the progress dialog. So I want to get the length. – Scorpion Jan 25 '13 at 05:05
  • As I said, this is not always possible. You can also check this answer for your puzzle: http://stackoverflow.com/questions/9728269/content-length-and-other-http-headers If you control the server, let the server respond with the right content length header, if not possible at all, the best way you can do then is to tell users how many bytes you have being received. If you pay attention to PC browser's downloading progress bar on some site, this is exactly what happened. – Jerry Tian Jan 25 '13 at 05:17

2 Answers2

1

It seems using HttpClient is a more straight way, which is also available on Android.

I will skip the auth part since your code looks fine to me, and based on your comment, the below downloading part of code should work. This is typical read&copy&write IO operation code, from my own project.

   HttpGet httpget = new HttpGet(url);
    //
    // Authorization configuration code here ...
    // 
    HttpResponse response = httpClient.execute(httpget);
    HttpEntity entity = response.getEntity();

    //
    // The content is all cached in memory for demonstration only, 
    // you can also write to file system using FileOutputStream.
    //
    ByteArrayOutputStream baos = new ByteArrayOutputStream();

    if (entity != null) {
        InputStream inputStream = entity.getContent();

        byte[] buf = new byte[1024];
        int read = 0;

        try {
            while ((read = is.read(buf)) >= 0) {
                baos.write(buf, 0, read);
            }
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        } finally {
            try {is.close();} catch (Exception ex) {}
            try {baos.close();} catch (Exception ex) {}
        }
    }

    byte[] content = baos.toByteArray();
Jerry Tian
  • 3,439
  • 4
  • 27
  • 29
0

I think you need to add connection.setDoInput(true) otherwise I'm pretty sure you can't get the input stream (you can have both doInput and doOutput true on the same connection) This may not answer your exact problem, but it will definitely prevent you from downloading.

Edit: not having setDoInput(true) may prevent downloading and I do not see that in your code

Matt
  • 690
  • 4
  • 10
  • it will definitely prevent you from downloading, I want to download the file Matt. As per your answer if I add that than it will not allow me to download than what is the meaning for it for me. Please kindly read the question once. – Scorpion Jan 23 '13 at 04:11