如何在安卓设备中使用Volley下载视频文件?

9

我希望使用Volley库下载视频。在我的应用程序中,我使用Volley库处理所有网络调用。

3个回答

9

首先,我们需要创建一个自定义类,它继承了Volley请求类。为了下载文件数据,我们可以创建一个自定义的字节数组请求。这个字节数组可以被转换成输入流,以便将数据写入SD卡。下面是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());
}
}

还有这个StringTokenizer类......StringTokenizer.java

(注:该链接为GitHub上的代码示例,需要自行跳转查看)

1
如果我想要下载 .apk 文件,那么我需要对您的解决方案进行哪些修改? - amit pandya

4
首先,您需要创建自己的自定义请求类,例如:
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));
    }
}

现在,您只需使用我们的自定义类通过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);

现在请前往应用程序文件夹 data/data//,您将会找到您需要的文件。您也可以将该文件下载至外部存储设备。
您可以使用以下方法访问内部存储设备中的文件。
Context.getFilesDir().<file_name>

从应用程序的内部目录中返回具有该名称的文件,如果没有这样的文件,则返回null。不要忘记添加文件扩展名。


1
openFileOutput(name, Context.MODE_PRIVATE)是什么? 这个方法在哪里? - Zia Ur Rahman
Context类 - Yehonatan

1

对于像我一样在寻找通用文件下载方法的人

理论上,这也可以作为这个问题的答案。


关于我的设置的快速信息

我正在使用AWS Lambda函数,该函数向我发送编码为base64的results.csv文件。

这是GET请求的函数返回的响应。

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

get_file()函数返回一个Base64编码的文件。

对Harshal Benake答案进行了一些微小的调整后,我能够将文件下载并保存在data/com.android.my_app/results.csv中。

以下是具体步骤:

创建一个自定义类用于请求,你可以在这里了解更多相关信息。

我复制了Harshal的答案并对其进行了修改,以创建我的自定义请求类EncodedByteArrayFileRequest.java

这是(稍微简化过的)源代码:

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

这是我如何使用这个类的方法:
首先,你需要在你的代码中某处初始化RequestQueue,通常是在onCreate方法中,所以它会像这样进行:
class MainActivity extends AppCompatActivity {

    private RequestQueue queue;

    @Override
    protected void onCreate() {

        // ...

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

    }

}


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

为了让代码更加简洁,我添加了 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;
}

最后,为了保存已下载的文件,这里有一个saveFile函数。这个函数基本上是复制粘贴。


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

}

为了我的目的,我下载并在不同的地方使用了多个不同的文件,因此要加载使用这个函数保存的文件,我使用getFilesDir().listFiles()并对其进行迭代,直到找到正确的文件名。然后可以使用一个简单的Scanner来读取文件。

可能有更好的方法来加载文件,但由于这对我有效,所以我决定将其保留。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接