简体   繁体   English

如何在android中使用volley下载视频文件?

[英]How to download video file using volley in android?

I want to download video using volley library.我想使用 volley 库下载视频。 I'm using volley library for all network calls in my application.我正在将 volley 库用于我的应用程序中的所有网络调用。

First, we need create a custom class which extends the Volley Request class.首先,我们需要创建一个自定义类来扩展 Volley Request 类。 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.该字节数组可以转换为将数据写入 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 文件显示了如何创建自定义字节数组请求并访问稍后将用于创建文件名的响应标头。

InputStreamVolleyRequest.java 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和这个 StringTokenizer 类..... 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.现在只需使用 Request.Method.GET 和您要下载文件的 url 通过我们的自定义类发送请求。

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.现在转到您的应用程序文件夹 data/data// 那里有您的文件,您也可以将文件下载到外部存储。

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.它从应用程序的内部目录中返回具有该名称的文件,如果没有具有该名称的文件,则返回 null。 Dont forget to add extenion of the file with the name.不要忘记使用名称添加文件的扩展名。

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.我正在使用一个 AWS lambda 函数,它向我发送一个以 base64 编码的results.csv文件。

This is the response returned by the function for a GET request.这是函数为 GET 请求返回的响应。

    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 get_file()函数返回一个 base64 编码的文件

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通过对 Harshal Benake 的回答稍作调整,我能够将文件下载并保存在 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我复制了 Harshal 的答案并对其进行了一些修改,以使我的自定义请求类称为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:首先,您需要在代码中的某处初始化 RequestQueue,通常是在您的 onCreate 方法中,所以它会是这样的:

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在 MainActivity 中,我创建了一个名为 sendGetRequest 的函数,它接收要下载的文件的文件名

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:为了使代码不那么混乱,我添加了handleResponsehandleErrorrequestParams并按照以下方式定义了它们:

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.最后,为了保存下载的文件,这里有一个saveFile函数。 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.出于我的目的,我下载了多个不同的文件并在不同的地方使用,因此要加载使用此函数保存的文件,我使用了getFilesDir().listFiles()并对其进行迭代,直到找到正确的文件名。 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.可能有更好的方法来加载文件,但由于这对我有用,所以我决定保持原样。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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