多线程下载

一、原理

二、示例代码

package com.shz;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;

public class Demo {

    // 定义将要开启的线程的数量
    private static int threadCount = 6;
    
    // 正在执行下载任务的线程总数
    private static int runningThreadCount = threadCount;
    
    public static void main(String[] args) throws Exception {
        String path = "http://192.168.1.101/MyWebSite/sogou.exe";
        URL url = new URL(path);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setReadTimeout(5000);
        conn.setRequestMethod("GET");
        int code = conn.getResponseCode();
        if (code == 200) {
            // 获取要下载的文件的大小
            int length = conn.getContentLength();
            System.out.println("文件大小:" + length);
            
            // 在客户端本地创建一个大小跟服务器文件大小一样的临时文件
            RandomAccessFile raf = new RandomAccessFile("sogou.exe", "rwd");
            raf.setLength(length);
            
            // 每个线程下载的部分文件大小(由于可能无法整除,最后一个线程下载的大小不一定为blockSize)
            int blockSize = length / threadCount;
            for (int threadId = 1; threadId <= threadCount; threadId++) {
                int startIndex = (threadId - 1) * blockSize;
                int endIndex = threadId * blockSize - 1;
                if (threadId == threadCount) {
                    endIndex = length;
                }

                System.out.println("线程" + threadId + ":" + startIndex + "--->"
                        + endIndex);
                
                new DownloadThread(path, startIndex, endIndex, threadId).start();
            }
        } else {
            System.out.println("连接服务器失败");
        }
    }

    public static String getNowString()
    {
        DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        return format.format(new Date());
    }
    
    private static class DownloadThread extends Thread {
        private String path;
        private int startIndex;
        private int endIndex;
        private int threadId;

        /**
         * 子线程下载的构造方法
         * 
         * @param path
         *            要下载的服务器文件路径
         * @param startIndex
         *            开始下载位置
         * @param endIndex
         *            结束下载位置
         * @param threadId
         *            线程Id
         */
        public DownloadThread(String path, int startIndex, int endIndex,
                int threadId) {
            this.path = path;
            this.startIndex = startIndex;
            this.endIndex = endIndex;
            this.threadId = threadId;
        }

        public void run() {
            try {
                
                // 判断是否存在和当前线程threadId同名的文件名称“x.txt”,
                // 如果存在,则读取文件中内容,该内容记录了上次线程已下载资源的总长度,那么当前线程开始下载的位置要加上此总长度
                File downloadFile = new File(threadId+".txt");
                if(downloadFile.exists() && downloadFile.length() > 0)
                {
                    FileInputStream fis = new FileInputStream(downloadFile);
                    byte[] buffer = new byte[1024];
                    int len = fis.read(buffer);
                    String strHasDownloadLen = new String(buffer,0,len);
                    System.out.println(getNowString()+":线程"+this.threadId+"上次已下载 "+strHasDownloadLen);
                    this.startIndex += Integer.parseInt(strHasDownloadLen);
                    fis.close();
                }
                
                System.out.println(getNowString()+":线程"+this.threadId+"开始下载...");                
                URL url = new URL(this.path);
                HttpURLConnection conn = (HttpURLConnection) url
                        .openConnection();
                conn.setReadTimeout(5000);
                conn.setRequestMethod("GET");
                conn.setRequestProperty("Range", "bytes=" + startIndex + "-"
                        + endIndex);

                // 如果是请求全部资源,服务器返回200;如果是请求部分资源,服务器返回206
                int code = conn.getResponseCode();
                if (code == 206) {
                    InputStream is = conn.getInputStream();
                    RandomAccessFile raf = new RandomAccessFile("sogou.exe",
                            "rwd");
                    raf.seek(this.startIndex);

                    int len = 0;
                    int hasDownloadLen = 0;
                    byte[] buffer = new byte[1024];
                    // 把当前线程已下载的资源总长度写到文件中
                    File downloadLenFile = new File(threadId+".txt");
                    while ((len = is.read(buffer)) > 0) {
                        raf.write(buffer, 0, len);
                        
                        // 记录当前线程已下载的资源总长度
                        hasDownloadLen += len;    
                        FileOutputStream fos = new FileOutputStream(downloadLenFile);
                        fos.write(String.valueOf(hasDownloadLen).getBytes());
                        fos.close();
                    }
                    
                    is.close();
                    raf.close();
                    System.out.println(getNowString()+":线程"+this.threadId+"下载结束...");
                } else {
                    System.out.println("线程" + this.threadId + "下载失败,服务器返回错误码:"
                            + code);
                }
            } catch (Exception e) {
                System.out.println("线程" + this.threadId + "下载出现异常:"
                        + e.toString());
            } finally {
                runningThreadCount --;
                
                if(runningThreadCount <= 0)
                {
                    for(int threadId=1;threadId<=threadCount;threadId++)
                    {
                        File file = new File(threadId+".txt");
                        if(file.exists())
                        {
                            file.delete();
                        }
                    }
                    
                    System.out.println(getNowString()+":文件下载结束");
                }
            }
        }
    }
}
View Code
原文地址:https://www.cnblogs.com/shaomenghao/p/3936091.html