Java之多线程断点下载的实现详解编程语言

RandomAccessFile类:
此类的实例支持对随机访问文件的读取和写入。随机访问文件的行为类似存储在文件系统中的一个大型 byte 数组。存在指向该隐含数组,光标或索引,称为文件指针;输入操作从文件指针开始读取字节,并随着对字节的读取而前移此文件指针。如果随机访问文件以读取/ 写入模式创建,则输出操作也可用;输出操作从文件指针开始写入字节,并随着对字节的写入而前移此文件指针。写入隐含数组的当前末尾之后的输出操作导致该数 组扩展。该文件指针可以通过 getFilePointer 方法读取,并通过 seek 方法设置。

下面有RandomAccessFile实现安卓下的断点下载的demo。
服务器端可以用tomcat模拟,将被下载的测试文件放入webApp/ROOT目录下即可。
先给出java借助HttpURLConnection类实现的多线程下载代码:

public class MultiThread { 
    private static int threadCount = 3; 
    private static long blockSize; 
    private static int runningThreadCount; 
    public static void main(String[] args) throws Exception { 
        String path = "http://10.0.67.172/test.exe"; 
        URL url = new URL(path);  
        HttpURLConnection conn = (HttpURLConnection) url.openConnection(); 
        conn.setRequestMethod("GET"); 
        conn.setConnectTimeout(5000);//超时时间 
        int code = conn.getResponseCode(); 
        System.out.println(code); 
 
        if(code / 100 == 2){ 
            int size = conn.getContentLength();//获取资源文件的长度 
            System.out.println("请求资源大小:" + size); 
            blockSize = size / threadCount;//将资源文件分为多少块,没一块的大小 
 
            runningThreadCount = threadCount; 
            long startIndex = 0; 
            long endIndex = 0; 
            //开启若干个子线程去实现多线程的下载 
            for(int i = 0; i < threadCount; i++){ 
                startIndex = i * blockSize; 
                endIndex = (i + 1) * blockSize - 1; 
                if(i == threadCount-1){ 
                    endIndex = size - 1; 
                } 
                System.out.println("开启线程:" + i + ";" + "开始位置:" + startIndex + ":" + "结束位置:" + endIndex); 
                new DownThread(path, startIndex, endIndex, i).start(); 
            } 
        } 
    } 
 
    private static class DownThread extends Thread{ 
        private String path; 
        private long startIndex; 
        private long endIndex; 
        private int threadId; 
 
        public DownThread(String path, long startIndex, long endIndex, int threadId) { 
            super(); 
            this.path = path; 
            this.startIndex = startIndex; 
            this.endIndex = endIndex; 
            this.threadId = threadId; 
        } 
 
        @Override 
        public void run() { 
            try { 
                URL url = new URL(path); 
                HttpURLConnection conn = (HttpURLConnection) url.openConnection(); 
                conn.setRequestMethod("GET"); 
                conn.setReadTimeout(5000); 
                conn.setRequestProperty("Range", "bytes=" + startIndex + "-" + endIndex);//设置服务器上的文件的读取位置 
 
                int code = conn.getResponseCode(); 
                if(code / 100 == 2){ 
                    InputStream is = conn.getInputStream(); 
                    File file = new File("temp.exe"); 
                    RandomAccessFile raf = new RandomAccessFile(file, "rw"); 
                    raf.seek(startIndex); 
                    System.out.println("第" + threadId + "个文件的开始位置:" + String.valueOf(startIndex)); 
                    int len = 0; 
                    byte[] buffer = new byte[1024]; 
                    while ((len = is.read(buffer)) != -1){ 
                        raf.write(buffer, 0, len);//写文件 
                    } 
                    raf.close(); 
                } 
            } catch (Exception e) { 
                e.printStackTrace(); 
            } 
        } 
    } 
}

断点下载的原理就是将上次文件下载的位置保存为临时文件,当完全下载完毕时再删除。

public class MultiThread { 
    private static int threadCount = 3; 
    private static long blockSize; 
    private static int runningThreadCount; 
    public static void main(String[] args) throws Exception { 
        String path = "http://10.0.67.172/test.rar"; 
        URL url = new URL(path);  
        HttpURLConnection conn = (HttpURLConnection) url.openConnection(); 
        conn.setRequestMethod("GET"); 
        conn.setConnectTimeout(5000);//超时时间 
        int code = conn.getResponseCode(); 
        System.out.println(code); 
 
        if(code / 100 == 2){ 
            int size = conn.getContentLength();//获取资源文件的长度 
            System.out.println("请求资源大小:" + size); 
            blockSize = size / threadCount;//将资源文件分为多少块,没一块的大小 
 
            runningThreadCount = threadCount; 
            long startIndex = 0; 
            long endIndex = 0; 
            for(int i = 0; i < threadCount; i++){ 
                startIndex = i * blockSize; 
                endIndex = (i + 1) * blockSize - 1; 
                if(i == threadCount-1){ 
                    endIndex = size - 1; 
                } 
                System.out.println("开启线程:" + i + ";" + "开始位置:" + startIndex + ":" + "结束位置:" + endIndex); 
                new DownThread(path, startIndex, endIndex, i).start(); 
            } 
        } 
    } 
 
    private static class DownThread extends Thread{ 
        private String path; 
        private long startIndex; 
        private long endIndex; 
        private int threadId; 
 
        public DownThread(String path, long startIndex, long endIndex, int threadId) { 
            super(); 
            this.path = path; 
            this.startIndex = startIndex; 
            this.endIndex = endIndex; 
            this.threadId = threadId; 
        } 
 
        @Override 
        public void run() { 
            int total = 0; 
            try { 
                File positionFile = new File(threadId + ".txt"); 
 
                URL url = new URL(path); 
                HttpURLConnection conn = (HttpURLConnection) url.openConnection(); 
                conn.setRequestMethod("GET"); 
                //接着上次的文件继续下载 
                if(positionFile.exists() && positionFile.length() > 0){ 
                    FileInputStream fis = new FileInputStream(positionFile); 
                    BufferedReader reader = new BufferedReader(new InputStreamReader(fis)); 
                    //获取当前线程上次下载的总大小是多少 
                    String lasttotalstr = reader.readLine(); 
                    int lastTotal = Integer.valueOf(lasttotalstr); 
                    System.out.println("上次线程下载的总大小:" + lastTotal); 
                    startIndex += lastTotal; 
                    total += lastTotal; 
                    fis.close(); 
                } 
                conn.setReadTimeout(5000); 
                conn.setRequestProperty("Range", "bytes=" + startIndex + "-" + endIndex);//设置服务器上的文件的读取位置 
 
                int code = conn.getResponseCode(); 
                if(code / 100 == 2){ 
                    InputStream is = conn.getInputStream(); 
                    File file = new File("temp.rar"); 
                    RandomAccessFile raf = new RandomAccessFile(file, "rw"); 
                    raf.seek(startIndex); 
                    System.out.println("第" + threadId + "个文件的开始位置:" + String.valueOf(startIndex)); 
                    int len = 0; 
                    byte[] buffer = new byte[1024]; 
                    while ((len = is.read(buffer)) != -1){ 
                        RandomAccessFile rf = new RandomAccessFile(positionFile, "rwd"); 
                        raf.write(buffer, 0, len);//写文件 
                        total += len; 
                        rf.write(String.valueOf(total).getBytes()); 
                        rf.close(); 
                    } 
                    is.close(); 
                    raf.close(); 
                } 
            } catch (Exception e) { 
                e.printStackTrace(); 
            }finally{ 
                synchronized (DownThread.class) { 
                    System.out.println("线程" + threadId + "下载完毕了"); 
                    runningThreadCount--; 
                    if (runningThreadCount < 1) { 
                        System.out.println("所有的线程都工作完毕了。删除临时记录的文件"); 
                        for (int i = 0; i < threadCount; i++) { 
                            File f = new File(i + ".txt"); 
                            System.out.println(f.delete()); 
                        } 
                    } 
                } 
            } 
        } 
    } 
}

原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/10770.html

(0)
上一篇 2021年7月19日
下一篇 2021年7月19日

相关推荐

发表回复

登录后才能评论