I want to download video using volley library. I'm using volley library for all network calls in my application.
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
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With