多线程断点下载

这篇是接着上一篇多线程下载文件(http://blog.csdn.net/wozuihaole/article/details/54892559)写的。我们下载文件中途如果断网了,文件只下载了一半,肯定是没法使用的,这时候我们就要用到断点下载,也就是下一次有网的时候继续下载完成文件。

原理:

1.为每个线程创建一个记录当前文件写入长度的临时文件;

2.每次线程请求服务器前先判断临时文件是否存在,如果存在,读出已写入的文件长度,使下载开始位置偏移到已写入长度的后面;

3.线程开始写入文件时,每次把buffer中的写入长度记录到临时文件中,保存起来;

4.当文件全部写入完成后,删除所有的临时文件。


主要代码:

1.每次请求服务器前,判断是否有临时文件存在:


if (positionFile.exists() && positionFile.length() >0) {//如果文件有内容,证明是断点继续下载
    BufferedReader reader = new BufferedReader(new FileReader(positionFile));
    int size = Integer.parseInt(reader.readLine());
    total += size;//将total的值加上已经写入的大小,再接着写的时候,大小就会接着增加了
    startIndex += size;//将下载的起始位置偏移到写过的内容后面
    Log.d(TAG,"之前写入了"+size+"个字节的内容");
    reader.close();//记得关哦
}

2.在每次写入下载文件的同时把写入的长度保存到临时文件,while循环里。

if (code == 206) {//分段下载的返回码是206不是200
    InputStream is = con.getInputStream();
    File f = new File(Environment.getExternalStorageDirectory(),"gg.exe");
    RandomAccessFile file = new RandomAccessFile(f,"rw");//在本地创建文件
    file.seek(startIndex);
    int len;
    byte[] buffer = new byte[512];
    while ((len = is.read(buffer)) != -1) {
        RandomAccessFile positionF = new RandomAccessFile(positionFile,"rwd");//以rwd的方式写入文件,rwd为实时保存数据到硬盘
        file.write(buffer,0,len);
        total += len;
        positionF.write(String.valueOf(total).getBytes());
        positionF.close();//用完别忘了关闭
    }
    is.close();
    file.close();
    Log.d(TAG,startIndex+"-"+endIndex+"部分文件下载完毕!");
}


3.在线程运行最后判断一下是否全部下载完成,也就是三个线程都运行完了,我是用的一个全局变量runThread记录当前运行的线程数,每运行完一个就减一,直到这个值为0,就是文件下载完成了,删除所有记录长度的临时文件。

finally {
    synchronized ("LOCK") {
        runThread--;
        Log.d(TAG,"线程"+threadId+"下载停止");
        if (runThread == 0) {
            //下载完成后把咱们用来记录每次写入大小的临时文件删除掉
            for (int i=1; i<= ThreadSize; i++) {
                File threadFile = new File(Environment.getExternalStorageDirectory(),i+".txt");
                Log.d(TAG,threadId+".txt文件删除"+threadFile.delete());
            }
            ToastOnScreen("下载完成");
        }
    }

完整代码:

package com.example.multhread;

import android.app.Activity;
import android.os.Environment;
import android.util.Log;
import android.widget.Toast;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.io.Reader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by pactera on 2017/2/4.
 */

public class DownloadManager {
    private static final String path = "http://10.5.92.12:8080/loginProject/ff.exe";
    private static final String TAG = "DownloadManager";
    private static final int ThreadSize = 3;
    private int runThread = ThreadSize;
    private Activity activity;
    private List<MyThread> threads;

    public DownloadManager(Activity activity) {
        this.activity = activity;
        threads = new ArrayList<MyThread>();
    }

    public void StartDownload () {
        //1.在Android客户端首先创建一个和需要下载的文件相同大小的空白文件
        long fileSize = getFileSize();
        //2.开启3个线程去下载对应文件
        if (fileSize > 0) {
            startMutileThread(fileSize);
            //3.如果所有线程都下载完毕,服务器的资源就下载完毕了
        } else {
            ToastOnScreen("服务器访问异常,请稍后再试");
        }
    }

    private long getFileSize() {
        try {
            URL url = new URL(path);
            HttpURLConnection con = (HttpURLConnection) url.openConnection();
            con.setRequestMethod("GET");
            con.setConnectTimeout(5000);
            int code = con.getResponseCode();

            if (code == 200) {
                long size = con.getContentLength();//获取文件长度
                Log.d(TAG,"文件大小为:"+size);
                File f = new File(Environment.getExternalStorageDirectory(),"gg.exe");
                RandomAccessFile file = new RandomAccessFile(f,"rwd");//在本地创建文件
                file.setLength(size);//将要下载的文件长度,赋给本地文件
                file.close();
                return size;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return 0;
    }

    private void startMutileThread(long fileSize) {
        long size = fileSize/ThreadSize;//获取每个线程需要下载文件的长度
        for (int i=0; i<ThreadSize; i++) {
            long startIndex = i*size;//计算每个线程开始下载文件的位置
            long endIndex = (i+1)*size-1;//停止下载文件的位置

            if (i==ThreadSize-1) {//如果是最后一个线程,则直接下载完剩余的文件
                endIndex = fileSize-1;
            }

            MyThread thread = new MyThread(startIndex,endIndex,(i+1));//开启线程
            threads.add(thread);
            thread.start();
        }
    }

    private class MyThread extends Thread {
        private long startIndex;
        private long endIndex;
        private int threadId;

        public MyThread(long startIndex, long endIndex, int threadId) {
            this.startIndex = startIndex;//开始下载的位置
            this.endIndex = endIndex;//停止下载的位置
            this.threadId = threadId;
            Log.d(TAG,startIndex+"-"+endIndex+"部分文件开始下载!");
        }

        @Override
        public void run() {
            super.run();
            try {
                int total = 0;//用来记录线程每次写入的数据长度
                File positionFile = new File(Environment.getExternalStorageDirectory(),threadId + ".txt");//创建记录每次写入数据长度的文件

                if (positionFile.exists() && positionFile.length() >0) {//如果文件有内容,证明是断点继续下载
                    BufferedReader reader = new BufferedReader(new FileReader(positionFile));
                    int size = Integer.parseInt(reader.readLine());
                    total += size;//将total的值加上已经写入的大小,再接着写的时候,大小就会接着增加了
                    startIndex += size;//将下载的起始位置偏移到写过的内容后面
                    Log.d(TAG,"之前写入了"+size+"个字节的内容");
                    reader.close();//记得关哦
                }

                URL url = new URL(path);
                HttpURLConnection con = (HttpURLConnection) url.openConnection();
                con.setRequestMethod("GET");
                con.setRequestProperty("Range","bytes="+startIndex+"-"+endIndex);//设置请求头,设置开始下载位置和停止位置
                con.setConnectTimeout(5000);
                int code = con.getResponseCode();

                if (code == 206) {//分段下载的返回码是206不是200
                    InputStream is = con.getInputStream();
                    File f = new File(Environment.getExternalStorageDirectory(),"gg.exe");
                    RandomAccessFile file = new RandomAccessFile(f,"rw");//在本地创建文件
                    file.seek(startIndex);
                    int len;
                    byte[] buffer = new byte[512];
                    while ((len = is.read(buffer)) != -1) {
                        RandomAccessFile positionF = new RandomAccessFile(positionFile,"rwd");//以rwd的方式写入文件,rwd为实时保存数据到硬盘
                        file.write(buffer,0,len);
                        total += len;
                        positionF.write(String.valueOf(total).getBytes());
                        positionF.close();//用完别忘了关闭
                    }
                    is.close();
                    file.close();
                    Log.d(TAG,startIndex+"-"+endIndex+"部分文件下载完毕!");
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                synchronized ("LOCK") {
                    runThread--;
                    Log.d(TAG,"线程"+threadId+"下载停止");
                    if (runThread == 0) {
                        //下载完成后把咱们用来记录每次写入大小的临时文件删除掉
                        for (int i=1; i<= ThreadSize; i++) {
                            File threadFile = new File(Environment.getExternalStorageDirectory(),i+".txt");
                            Log.d(TAG,threadId+".txt文件删除"+threadFile.delete());
                        }
                        ToastOnScreen("下载完成");
                    }
                }
            }
        }
    }

    private void ToastOnScreen(final String s) {
        activity.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                Toast.makeText(activity, s, Toast.LENGTH_LONG).show();
            }
        });
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值