全栈开发知识库

多线程断点下载

  1. 多线程下载 ```java public class MultiThreadDownloader {

    private URL url;        // 目标地址
    private File file;        // 本地文件
    private long threadLen;    // 每个线程下载多少
    
    private static final int THREAD_AMOUNT = 3;                // 线程数
    private static final String DIR_PATH = "F:/Download";    // 下载目录
    
    public MultiThreadDownloader(String address) throws IOException {    
        // 记住下载地址
        url = new URL(address);                                                            
        // 截取地址中的文件名, 创建本地文件
        file = new File(DIR_PATH, address.substring(address.lastIndexOf("/") + 1));        
    }
    
    public void download() throws IOException {
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setConnectTimeout(3000);
    
        // 获取文件总长度
        long totalLen = conn.getContentLength();                    
        // 计算每个线程要下载的长度
        // 总长度 如果能整除 线程数, 每条线程下载的长度就是 总长度 / 线程数
        // 总长度 如果不能整除 线程数, 那么每条线程下载长度就是 总长度 / 线程数 + 1
        threadLen = (totalLen + THREAD_AMOUNT - 1) / THREAD_AMOUNT;
    
        // 在本地创建一个和服务端大小相同的文件
        RandomAccessFile raf = new RandomAccessFile(file, "rw");    
        // 设置文件的大小, 写入了若干个0
        raf.setLength(totalLen);                                    
        raf.close();
    
        // 按照线程数循环
        for (int i = 0; i < THREAD_AMOUNT; i++) {
            // 开启线程, 每个线程将会下载一部分数据到本地文件中 
            new DownloadThread(i).start();       
        }     
    }
    
    private class DownloadThread extends Thread {
        // 用来标记当前线程是下载任务中的第几个线程
        private int id;
    
        public DownloadThread(int id) {
            this.id = id;
        }
    
        public void run() {
            // 从临时文件读取当前线程已完成的进度
    
            long start = id * threadLen;     
            long end = id * threadLen + threadLen - 1;
            System.out.println("线程" + id + ": " + start + "-" + end);
    
            try {
                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                conn.setConnectTimeout(3000);
                // 设置当前线程下载的范围(start和end都包含)
                conn.setRequestProperty("Range", "bytes=" + start + "-" + end);
    
                InputStream in = conn.getInputStream();
                // 随机读写文件, 用来向本地文件写出
                RandomAccessFile raf = new RandomAccessFile(file, "rw");        
                // 设置保存数据的位置
                raf.seek(start);
    
                // 每次拷贝100KB
                byte[] buffer = new byte[1024 * 100];    
                int len;
                while ((len = in.read(buffer)) != -1) {
                    // 从服务端读取数据, 写到本地文件
                    raf.write(buffer, 0, len);            
                }
                raf.close();
    
                System.out.println("线程" + id + "下载完毕");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    
    public static void main(String[] args) throws IOException {
        new MultiThreadDownloader("http://192.168.1.240:8080/14.Web/android-sdk_r17-windows.zip").download();
    }
    

    } ```

  2. 断点下载 ```java public class BreakpointDownloader { // 下载目录 private static final String DIR_PATH = "F:/Download";
    // 总线程数 private static final int THREAD_AMOUNT = 3;

    // 目标下载地址
    private URL url;            
    // 本地文件
    private File dataFile;        
    // 用来存储每个线程下载的进度的临时文件
    private File tempFile;        
    // 每个线程要下载的长度
    private long threadLen;      
    // 总共完成了多少  
    private long totalFinish;    
    // 服务端文件总长度
    private long totalLen;        
    // 用来记录开始下载时的时间
    private long begin;
    
    public BreakpointDownloader(String address) throws IOException {    
        url = new URL(address);                                                            
        dataFile = new File(DIR_PATH, address.substring(address.lastIndexOf("/") + 1));    
        tempFile = new File(dataFile.getAbsolutePath() + ".temp");                        
    }
    
    public void download() throws IOException {
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setConnectTimeout(3000);
    
        totalLen = conn.getContentLength();                                    
        threadLen = (totalLen + THREAD_AMOUNT - 1) / THREAD_AMOUNT;
    
        if (!dataFile.exists()) {                                            
            RandomAccessFile raf = new RandomAccessFile(dataFile, "rws");    
            raf.setLength(totalLen);                                        
            raf.close();
        }
    
        if (!tempFile.exists()) {                                            
            RandomAccessFile raf = new RandomAccessFile(tempFile, "rws");    
            for (int i = 0; i < THREAD_AMOUNT; i++)                           
                raf.writeLong(0);                                            
            raf.close();
        }
    
        for (int i = 0; i < THREAD_AMOUNT; i++) { 
            new DownloadThread(i).start();       
        }
    
        // 记录开始时间
        begin = System.currentTimeMillis();        
    }
    
    private class DownloadThread extends Thread {
        // 用来标记当前线程是下载任务中的第几个线程
        private int id;
    
        public DownloadThread(int id) {
            this.id = id;
        }
    
        public void run() {
            try {
                RandomAccessFile tempRaf = new RandomAccessFile(tempFile, "rws");        
                tempRaf.seek(id * 8);                        
                long threadFinish = tempRaf.readLong();        
                synchronized(BreakpointDownloader.this) {    
                    totalFinish += threadFinish;            
                }
    
                long start = id * threadLen + threadFinish;      
                long end = id * threadLen + threadLen - 1;       
                System.out.println("线程" + id + ": " + start + "-" + end);
    
                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                conn.setConnectTimeout(3000);
                conn.setRequestProperty("Range", "bytes=" + start + "-" + end);
    
                InputStream in = conn.getInputStream();                              
                RandomAccessFile dataRaf = new RandomAccessFile(dataFile, "rws");    
                dataRaf.seek(start);
    
                byte[] buffer = new byte[1024 * 100];            
                int len;
                while ((len = in.read(buffer)) != -1) {
                    dataRaf.write(buffer, 0, len);               
                    threadFinish += len;                        
                    tempRaf.seek(id * 8);                        
                    tempRaf.writeLong(threadFinish);           
                    synchronized(BreakpointDownloader.this) {   
                        totalFinish += len;                        
                    }
                }
                dataRaf.close();
                tempRaf.close();
    
                System.out.println("线程" + id + "下载完毕");
                // 如果已完成长度等于服务端文件长度(代表下载完成)
                if (totalFinish == totalLen) {                    
                    System.out.println("下载完成, 耗时: " + (System.currentTimeMillis() - begin));
                    // 删除临时文件
                    tempFile.delete();                            
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    
    public static void main(String[] args) throws IOException {
        new BreakpointDownloader("http://192.168.1.240:8080/14.Web/android-sdk_r17-windows.zip").download();
    }
    

    } ```


  • 邮箱 :charon.chui@gmail.com
  • Good Luck!