美文网首页
Android 文件下载、多线程下载、多线程断点下载

Android 文件下载、多线程下载、多线程断点下载

作者: JuSong | 来源:发表于2017-03-04 13:59 被阅读0次

    1.普通单线程下载文件:

     

    直接使用URLConnection.openStream();打开网络输入流,然后将流写入到文件中。

     

    核心方法

     

    public static void downLoad(String path,Context context)throws Exception
    {
    URL url = new URL(path);
    InputStream is = url.openStream();
    //截取最后的文件名
    String end = path.substring(path.lastIndexOf("."));
    //打开手机对应的输出流,输出到文件中
    OutputStream os = context.openFileOutput("Cache_"+System.currentTimeMillis()+end, Context.MODE_PRIVATE);
    byte[] buffer = new byte[1024];
    int len = 0;
    //从输入六中读取数据,读到缓冲区中
    while((len = is.read(buffer)) > 0)
    {
    os.write(buffer,0,len);
    }
    //关闭输入输出流
    is.close();
    os.close();
    }

     

     

    2.普通多线程下载:

     

    多线程下载的流程:

    1.获取网络连接

    2.本地磁盘创建相同大小的空文件

    3.计算每条线程从哪个部分开始下载,结束

    4.依次创建,启动多条线程来下载网络资源的指定部分

     

     

    (1)根据要访问的URL路径去调用openConnection(),得到HttpConnection对象,接着调用getContentLength(),获得下载的文件长度,最后设置本地文件长度。

    int filesize=HttpURLConnection.getContentLength();

    RandomAccessFile file=new RandomAccessFile("xx.apk","rwd");

    file.setLength(filesize);

     

    另外:RandomAccessFile:随机访问类,同时整合了FileOutputStream和FileInputStream,支持从文件的任何字节处读写数据。而File只支持将文件作为整体来处理,不能读写文件。

     

    (2)根据文件长度以及线程数计算每条线程的下载长度,以及每条线程下载的开始位置。

    计算公式:加入N条线程,下载大小为M个字节的文件:

    每个线程下载的数据量:M%N==0?M/N:M/N+1

    比如:大小为10个字节,开三条线程,每条线程:

    10/3+1=4;下载量;4/4/3

    下载开始位置的计算:

    假设线程id:1,2,3

    开始位置:id*下载量

    结束为止:(id+1)*下载量-1(最后一条位置是不用计算结束位置的)

    (3)保存文件。使用RandomAccessFile类指定从文件的什么位置写入数据。

    RandomAccessFile file=new RandomAccessFile(“XXX.APK”,“rwd”);

    file.seek(2097152);

     

    另外在下载时,怎么指定每条线程,开始下载的位置呢?

    HTTP协议为我们提供了一个Range头,使用下面的方法指定从什么位置开始下载:

    HTTPURLConnection.setRequestProperty(“Range”,“byte=2097152”);

     

    public class Downloader {

    public void downloader() throws Exception {
    //设置url地址和下载后的文件名
    String filename="meitu.exe";
    String path="http://10.13.20.32:8080/Test/XiuXiu_Green.exe";
    URL url=new URL(path);
    HttpURLConnection httpURLConnection= (HttpURLConnection) url.openConnection();
    httpURLConnection.setConnectTimeout(5000);
    httpURLConnection.setRequestMethod("GET");

    //获得需要下载的文件的长度
    int filelength=httpURLConnection.getContentLength();

    //本地创建一个大小相同的文件夹
    RandomAccessFile randomAccessFile=new RandomAccessFile(filename,"rwd");
    randomAccessFile.setLength(filelength);
    randomAccessFile.close();

    httpURLConnection.disconnect();

    //设置线程的下载的数量
    int threadSize=3;
    //计算每条线程下载的数量
    int threadlength = filelength % threadSize == 0 ? filelength/threadSize:filelength+1;

    //设置每条线程从哪个位置开始下载
    for (int i=0;i<threadSize;i++){
    int startPoint=i*threadlength;
    //从文件的什么位置开始写入
    RandomAccessFile file=new RandomAccessFile(filename,"rwd");
    randomAccessFile.seek(startPoint);
    //开启三条线程,开始从startPoint下载文件
    new DownLoadThread (i,startPoint,file,threadlength,path).start();
    }

    int quit = System.in.read();
    while('q' != quit)
    {
    Thread.sleep(2000);
    }



    }

    private class DownLoadThread extends Thread{
    private int i;
    private int startPoint;
    private RandomAccessFile file;
    private int threadlength;
    private String path;
    public DownLoadThread() {

    }
    public DownLoadThread (int i, int startPoint, RandomAccessFile file, int threadlength, String path) {
    this.i=i;
    this.file=file;
    this.threadlength=threadlength;
    this.startPoint=startPoint;
    this.path=path;
    }
    @Override
    public void run() {
    try {
    URL url=new URL(path);
    HttpURLConnection conn= (HttpURLConnection) url.openConnection();
    conn.setRequestMethod("GET");
    conn.setConnectTimeout(5000);
    //指定从什么地方开始下载
    conn.setRequestProperty("Range", "bytes="+startPoint+"-");
    if(conn.getResponseCode()==206){
    InputStream in=conn.getInputStream();
    byte[] by=new byte[1024];
    int len=-1;
    int length=0;
    while (length<threadlength&&(len=in.read(by))!=-1){
    file.write(by,0,len);
    //计算累计下载的长度
    length+=len;
    }
    file.close();
    in.close();
    System.out.println("线程"+(i+1) + "已下载完成");
    }


    } catch (java.io.IOException e) {
    System.out.println("线程"+(i+1) + "下载出错"+ e);}
    }
    }

     

     

     

    int filelength = conn.getContentLength(); //获得下载文件的长度(大小)

    RandomAccessFile file = new RandomAccessFile(filename, "rwd"); //该类运行对文件进行读写,是多线程下载的核心

    nt threadlength = filelength % 3 == 0 ? filelength/3:filelength+1; //计算每个线程要下载的量

    conn.setRequestProperty("Range", "bytes="+startposition+"-"); //指定从哪个位置开始读写,这个是URLConnection提供的方法

    //System.out.println(conn.getResponseCode()); //这个注释了的代码是用来查看conn的返回码的,我们前面用的都是200, 而针对多线程的话,通常是206,必要时我们可以通过调用该方法查看返回码!

    int quit = System.in.read();while('q' != quit){Thread.sleep(2000);} //这段代码是做延时操作的,因为我们用的是本地下载,可能该方法运行完了,而我们的 线程还没有开启,这样会引发异常,这里的话,让用户输入一个字符,如果是'q'的话就退出

     

    .使用DownloadManager更新应用并覆盖安装:

    下面的代码可以直接用,加入到项目后,记得为这个内部广播注册一个过滤器:

     

    public class UpdateAct extends AppCompatActivity {
    //这个更新的APK的版本部分,我们是这样命名的:xxx_v1.0.0_xxxxxxxxx.apk
    //这里我们用的是git提交版本的前九位作为表示
    private static final String FILE_NAME = "ABCDEFGHI";

    @Override
    public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    String endpoint = "";
    try {
    //这部分是获取AndroidManifest.xml里的配置信息的,包名,以及Meta_data里保存的东西
    ApplicationInfo info = getPackageManager().getApplicationInfo(
    getPackageName(), PackageManager.GET_META_DATA);
    //我们在meta_data保存了xxx.xxx这样一个数据,是https开头的一个链接,这里替换成http
    endpoint = info.metaData.getString("xxxx.xxxx").replace("https",
    "http");
    } catch (PackageManager.NameNotFoundException e) {
    e.printStackTrace();
    }
    //下面的都是拼接apk更新下载url的,path是保存的文件夹路径
    final String _Path = this.getIntent().getStringExtra("path");
    final String _Url = endpoint + _Path;
    final DownloadManager _DownloadManager = (DownloadManager) getSystemService(DOWNLOAD_SERVICE);
    DownloadManager.Request _Request = new DownloadManager.Request(
    Uri.parse(_Url));
    _Request.setDestinationInExternalPublicDir(
    Environment.DIRECTORY_DOWNLOADS, FILE_NAME + ".apk");
    _Request.setTitle(this.getString(R.string.app_name));
    //是否显示下载对话框
    _Request.setShowRunningNotification(true);
    _Request.setMimeType("application/com.trinea.download.file");
    //将下载请求放入队列
    _DownloadManager.enqueue(_Request);
    this.finish();
    }



    //注册一个广播接收器,当下载完毕后会收到一个android.intent.action.DOWNLOAD_COMPLETE
    //的广播,在这里取出队列里下载任务,进行安装
    public static class Receiver extends BroadcastReceiver {
    public void onReceive(Context context, Intent intent) {
    final DownloadManager _DownloadManager = (DownloadManager) context
    .getSystemService(Context.DOWNLOAD_SERVICE);
    final long _DownloadId = intent.getLongExtra(
    DownloadManager.EXTRA_DOWNLOAD_ID, 0);
    final DownloadManager.Query _Query = new DownloadManager.Query();
    _Query.setFilterById(_DownloadId);
    final Cursor _Cursor = _DownloadManager.query(_Query);
    if (_Cursor.moveToFirst()) {
    final int _Status = _Cursor.getInt(_Cursor
    .getColumnIndexOrThrow(DownloadManager.COLUMN_STATUS));
    final String _Name = _Cursor.getString(_Cursor
    .getColumnIndexOrThrow("local_filename"));
    if (_Status == DownloadManager.STATUS_SUCCESSFUL
    && _Name.indexOf(FILE_NAME) != 0) {

    Intent _Intent = new Intent(Intent.ACTION_VIEW);
    _Intent.setDataAndType(
    Uri.parse(_Cursor.getString(_Cursor
    .getColumnIndexOrThrow(DownloadManager.COLUMN_LOCAL_URI))),
    "application/vnd.android.package-archive");
    _Intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    context.startActivity(_Intent);
    }
    }
    _Cursor.close();
    }
    }
    }

     

     

    多线程断点下载:

    附上一篇讲解线程文件下载、多线程下载的链接:

    http://www.runoob.com/w3cnote/android-tutorial-download2.html

    相关文章

      网友评论

          本文标题:Android 文件下载、多线程下载、多线程断点下载

          本文链接:https://www.haomeiwen.com/subject/skhrgttx.html