Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to download video file using volley in android?

Tags:

android

I want to download video using volley library. I'm using volley library for all network calls in my application.

like image 458
Yogesh Avatar asked Aug 20 '16 08:08

Yogesh


3 Answers

First, we need create a custom class which extends the Volley Request class. To download the file data we can create a custom byte array request. This byte array can be converted into inputstream that will write data to the SDCard. Following InputStreamVolleyRequest.java file shows how to create a custom byte array request and access response headers that will be later used to create the file name.

InputStreamVolleyRequest.java

import com.android.volley.AuthFailureError;
import com.android.volley.NetworkResponse;
import com.android.volley.Request;
import com.android.volley.Response;
import com.android.volley.toolbox.HttpHeaderParser;
import java.util.HashMap;
import java.util.Map;

public class InputStreamVolleyRequest extends Request<byte[]> {
private final Response.Listener<byte[]> mListener;
private Map<String, String> mParams;
//create a static map for directly accessing headers
public Map<String, String> responseHeaders ;

public InputStreamVolleyRequest(int post, String mUrl,Response.Listener<byte[]> listener,
                                Response.ErrorListener errorListener, HashMap<String, String> params) {
    // TODO Auto-generated constructor stub

     super(post, mUrl, errorListener);
     // this request would never use cache.
     setShouldCache(false);
     mListener = listener;
     mParams=params;
}

@Override
protected Map<String, String> getParams()
        throws com.android.volley.AuthFailureError {
    return mParams;
};


@Override
protected void deliverResponse(byte[] response) {
    mListener.onResponse(response);
}

@Override
protected Response<byte[]> parseNetworkResponse(NetworkResponse response) {

    //Initialise local responseHeaders map with response headers received
    responseHeaders = response.headers;

    //Pass the response data here
    return Response.success(response.data, HttpHeaderParser.parseCacheHeaders(response));
}

}

Your Activity class.....

import android.os.Environment;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.Response.ErrorListener;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.HurlStack;
import com.android.volley.toolbox.Volley;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;


public class FileDownloadActivity extends ActionBarActivity implements Response.Listener<byte[]>, ErrorListener{
Button btn_download;
InputStreamVolleyRequest request;
int count;
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_file_download);
    btn_download =(Button)findViewById(R.id.button);
    btn_download.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {

            //Change your url below
            String mUrl="http://yoururl.com";
            request = new InputStreamVolleyRequest(Request.Method.GET, mUrl, FileDownloadActivity.this, FileDownloadActivity.this, null);
            RequestQueue mRequestQueue = Volley.newRequestQueue(getApplicationContext(),
                    new HurlStack());
            mRequestQueue.add(request);
        }
    });
}

@Override
public void onResponse(byte[] response) {
    HashMap<String, Object> map = new HashMap<String, Object>();
    try {
        if (response!=null) {

            //Read file name from headers
            String content =request.responseHeaders.get("Content-Disposition")
                    .toString();
            StringTokenizer st = new StringTokenizer(content, "=");
            String[] arrTag = st.toArray();

            String filename = arrTag[1];
            filename = filename.replace(":", ".");
            Log.d("DEBUG::RESUME FILE NAME", filename);

            try{
                long lenghtOfFile = response.length;

                //covert reponse to input stream
                InputStream input = new ByteArrayInputStream(response);
                File path = Environment.getExternalStorageDirectory();
                File file = new File(path, filename);
                map.put("resume_path", file.toString());
                BufferedOutputStream output = new BufferedOutputStream(new FileOutputStream(file));
                byte data[] = new byte[1024];

                long total = 0;

                while ((count = input.read(data)) != -1) {
                    total += count;
                    output.write(data, 0, count);
                }

                output.flush();

                output.close();
                input.close();
            }catch(IOException e){
                e.printStackTrace();

            }
        }
    } catch (Exception e) {
        // TODO Auto-generated catch block
        Log.d("KEY_ERROR", "UNABLE TO DOWNLOAD FILE");
        e.printStackTrace();
    }
}

@Override
public void onErrorResponse(VolleyError error) {
    Log.d("KEY_ERROR", "UNABLE TO DOWNLOAD FILE. ERROR:: "+error.getMessage());
}
}

and this StringTokenizer class..... StringTokenizer.java

like image 80
yosef abu rabeaa Avatar answered Nov 12 '22 14:11

yosef abu rabeaa


Firstly you have to create your own custom request class like,

class InputStreamVolleyRequest extends Request<byte[]> {
    private final Response.Listener<byte[]> mListener;
   private Map<String, String> mParams;

    //create a static map for directly accessing headers
   public Map<String, String> responseHeaders ;

    public InputStreamVolleyRequest(int method, String mUrl ,Response.Listener<byte[]> listener,
                                    Response.ErrorListener errorListener, HashMap<String, String> params) {
      // TODO Auto-generated constructor stub

        super(post, mUrl, errorListener);
         // this request would never use cache.
         setShouldCache(false);
         mListener = listener;
         mParams=params;
   }

   @Override
   protected Map<String, String> getParams()
         throws com.android.volley.AuthFailureError {
      return mParams;
   };


    @Override
    protected void deliverResponse(byte[] response) {
        mListener.onResponse(response);
    }

    @Override
    protected Response<byte[]> parseNetworkResponse(NetworkResponse response) {

        //Initialise local responseHeaders map with response headers received
        responseHeaders = response.headers;

        //Pass the response data here
        return Response.success( response.data, HttpHeaderParser.parseCacheHeaders(response));
    }
}

Now just send request though our custom class with Request.Method.GET and the url from where you want to download file.

String mUrl= <YOUR_URL>;
InputStreamVolleyRequest request = new InputStreamVolleyRequest(Request.Method.GET, mUrl,
        new Response.Listener<byte[]>() { 
             @Override 
             public void onResponse(byte[] response) { 
           // TODO handle the response 
            try { 
            if (response!=null) {

              FileOutputStream outputStream;
              String name=<FILE_NAME_WITH_EXTENSION e.g reference.txt>;
                outputStream = openFileOutput(name, Context.MODE_PRIVATE);
                outputStream.write(response);
                outputStream.close();
                Toast.makeText(this, "Download complete.", Toast.LENGTH_LONG).show();
            }
        } catch (Exception e) {
            // TODO Auto-generated catch block
            Log.d("KEY_ERROR", "UNABLE TO DOWNLOAD FILE");
            e.printStackTrace();
        }
  }
} ,new Response.ErrorListener() {

  @Override
  public void onErrorResponse(VolleyError error) {
    // TODO handle the error
    error.printStackTrace();
  }
}, null);
          RequestQueue mRequestQueue = Volley.newRequestQueue(getApplicationContext(), new HurlStack());
          mRequestQueue.add(request);

Now go to your application folder data/data// and there is your file you can also download the file to external stoage.

You can access the files in internal storage using

Context.getFilesDir().<file_name>

It returns the file with that name from the internal directory of application and null if there is no file with such name. Dont forget to add extenion of the file with the name.

like image 44
Harshal Benake Avatar answered Nov 12 '22 14:11

Harshal Benake


For those like me who found this answer while looking for a way to download a file in general

This, in theory, should also work as an answer for this question as well.


Quick info about my setup

I'm using an AWS lambda function which sends me a results.csv file encoded in base64.

This is the response returned by the function for a GET request.

    response = {
        'statusCode': 200,
        'headers': {
            'content-type': 'text/csv'
        },
        'msg': 'Successfully got GET request',
        'body': get_file(filename=filename),
        'isBase64Encoded': True
    }

the get_file() function returns a base64 encoded file

With some slight tweaks to Harshal Benake's Answer, I was able to download and save the file in data/com.android.my_app/results.csv

Here's how it went

Create a custom class for your Request, which you can learn more about here

I copied Harshal's answer and modified it a bit to make my custom request class called EncodedByteArrayFileRequest.java

This is the (slightly simplified) source code

package com.android.resultsgrapherlive;

import com.android.volley.AuthFailureError;
import com.android.volley.NetworkResponse;
import com.android.volley.Request;
import com.android.volley.Response;
import com.android.volley.toolbox.HttpHeaderParser;

import java.util.HashMap;
import java.util.Map;

class EncodedByteArrayFileRequest extends Request<byte[]> {
    private final Response.Listener<byte[]> mListener;
    private Map<String, String> mParams;

    public Map<String, String> responseHeaders;

    public EncodedByteArrayFileRequest(int method, String mUrl,
                                       Response.Listener<byte[]> listener,
                                       Response.ErrorListener errorListener,
                                       HashMap<String, String> params)
   {
        // TODO Auto-generated constructor stub

        super(method, mUrl, errorListener);
        // this request would never use cache.
        setShouldCache(false);
        mListener = listener;
        mParams = params;
    }

    // NOTE: original answer had this method set as protected
    @Override
    public Map<String, String> getHeaders() throws AuthFailureError {
        return mParams;
    }

    @Override
    protected void deliverResponse(byte[] response) {
        mListener.onResponse(response);
    }

    @Override
    protected Response<byte[]> parseNetworkResponse(NetworkResponse response) {

        responseHeaders = response.headers;

        return Response.success(response.data, HttpHeaderParser.parseCacheHeaders(response));
    }
}

Here's how I used this class

First of all, you need to have RequestQueue intialized somewhere in your code, usually in your onCreate method, so it would go something like this:

class MainActivity extends AppCompatActivity {

    private RequestQueue queue;

    @Override
    protected void onCreate() {

        // ...

        queue = new Volley.newRequestQueue(this);
        // ...

    }

}

Witihin MainActivity, I made a function called sendGetRequest, which takes in a filename for which file to download

private void sendGetRequest(final String filename) {
    String url = "add.yourownurl.com";

    EncodedByteArrayFileRequest request = new EncodedByteArrayFileRequest(
            Request.Method.GET, url,
            new Response.Listener<byte[]>() {
                @Override
                public void onResponse(byte[] response) {

                    handleResponse(response, filename);
                }
            },
            new Response.ErrorListener() {
                @Override
                public void onErrorResponse(VolleyError error) {
                    handleError(error);
                }
            },
            (HashMap<String, String>) requestParams(filename)
    );

    queue.add(request);
}

To make the code less messy, I added handleResponse, handleError and requestParams and defined them something along these lines:

private void handleResponse(byte[] response, String saveAs) {

    try {

        saveFile(response, saveAs);

    } catch (IOException e) {

        // TODO: remove all logs before finalizing app

        Log.d("failed to save file", "File not saved", e);

        String msg = "Failed to save file";
        Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
    }

}



private void handleError(VolleyError error) {

    Log.d("VolleyError", "Got error in get request", error);

    Toast.makeText(this, "Error. Check Logcat", Toast.LENGTH_SHORT).show();

}



private Map<String, String> requestParams(String filename) {

    Map<String, String> params = new HashMap<>();
    params.put("filename", filename);    // I setup my lambda function to check for this header

    return params;
}

Finally, to save the downloaded file, here's a saveFile function. This one is pretty much a copypaste.


private void saveFile(byte[] response, String filename) throws IOException {
    FileOutputStream outputStream;
    outputStream = openFileOutput(filename, Context.MODE_PRIVATE);
    outputStream.write(response);
    outputStream.close();

}

For my purposes, I had multiple different files downloaded and used in different places, So to load a file saved using this function, I used getFilesDir().listFiles() and iterated over it to until I found the correct filename. Then a simple Scanner can be used to read the file.

There's probably better ways to load a file but since this worked for me, I decided to leave it like this.

like image 44
aziznal Avatar answered Nov 12 '22 14:11

aziznal