多线程下载实例

点击下载MultiThreadDownload.txt

一个简单的HttpURLConnection多线程下载实例,没有做断点续传,没有实现线程池,会根据网络状况自动分配线程数

/**
* 简单的多线程下载(不支持断点,无线程池)
*
* @Title MulThreadDownload
* @Description 简单的多线程下载
* @author Andy
*/
public class MulThreadDownload {
    /** 下载的URL */
    private URL downloadUrl;
    /** 本地文件 */
    private File localFile;
    /** 每个线程下载的数据长度 */
    private int block;

    /**
     *
     * 多线程文件下载(根据网络状况自动分配线程数)
     *
     * @param path
     *            下载地址
     * @param locDirPath
     *            本地目录
     * @param downloadCallBack
     *            回调
     * @param fileTypes
     *            指定文件类型(可选)
     * @throws Exception
     */
    public void download(Context context, String path, String locDirPath,
            DownloadCallBack downloadCallBack, String... fileTypes)
            throws Exception {
        int threadCount = 3;
        try {
            ConnectivityManager connectivityManager = (ConnectivityManager) context
                    .getSystemService(CONNECTIVITY_SERVICE);
            NetworkInfo info = connectivityManager.getActiveNetworkInfo();
            if (info == null || !info.isConnectedOrConnecting()) {
                threadCount = 3;
                return;
            }
            switch (info.getType()) {
            case ConnectivityManager.TYPE_WIFI:
            case ConnectivityManager.TYPE_WIMAX:
            case ConnectivityManager.TYPE_ETHERNET:
                threadCount = 4;
                break;
            case ConnectivityManager.TYPE_MOBILE:
                switch (info.getSubtype()) {
                case TelephonyManager.NETWORK_TYPE_LTE: // 4G
                case TelephonyManager.NETWORK_TYPE_HSPAP:
                case TelephonyManager.NETWORK_TYPE_EHRPD:
                    threadCount = 3;
                    break;
                case TelephonyManager.NETWORK_TYPE_UMTS: // 3G
                case TelephonyManager.NETWORK_TYPE_CDMA:
                case TelephonyManager.NETWORK_TYPE_EVDO_0:
                case TelephonyManager.NETWORK_TYPE_EVDO_A:
                case TelephonyManager.NETWORK_TYPE_EVDO_B:
                    threadCount = 2;
                    break;
                case TelephonyManager.NETWORK_TYPE_GPRS: // 2G
                case TelephonyManager.NETWORK_TYPE_EDGE:
                    threadCount = 1;
                    break;
                default:
                    threadCount = 3;
                }
                break;
            default:
                threadCount = 3;
            }
        } catch (Exception e) {
            threadCount = 3;
        }
        download(path, locDirPath, threadCount, downloadCallBack, fileTypes);
    }

    /**
     *
     * 多线程文件下载
     *
     * @param path
     *            下载地址
     * @param locDirPath
     *            本地目录
     * @param threadCount
     *            线程数
     * @param downloadCallBack
     *            回调
     * @param fileTypes
     *            指定文件类型(可选)
     * @throws Exception
     */
    public void download(final String path, final String locDirPath,
            final int threadCount, final DownloadCallBack downloadCallBack,
            final String... fileTypes) {
        System.out.println("线程" + "主线程开始=" + path);
        new Thread() {
            public void run() {
                try {
                    downloadThread(path, locDirPath, threadCount,
                            downloadCallBack, fileTypes);
                } catch (Exception e) {
                    e.printStackTrace();
                    System.out.println("线程" + "子线程开始=" + e);
                    downloadCallBack.error();
                }
            };
        }.start();
    }

    /**
     *
     * 多线程文件下载
     *
     * @param path
     *            下载地址
     * @param locDirPath
     *            本地目录
     * @param threadCount
     *            线程数
     * @param downloadCallBack
     *            回调
     * @param fileTypes
     *            指定文件类型
     * @throws Exception
     */
    private void downloadThread(String path, String locDirPath,
            final int threadCount, final DownloadCallBack downloadCallBack,
            String... fileTypes) throws Exception {
        downloadUrl = new URL(path);
        HttpURLConnection conn = (HttpURLConnection) downloadUrl
                .openConnection();
        // 设置 GET 请求方式
        conn.setRequestMethod("GET");
        // 设置响应超时
        conn.setConnectTimeout(5 * 1000);
        // 获取下载文件大小
        final int len = conn.getContentLength();
        System.out.println("线程" + "len=" + len);
        if (len <= 0) {
            // 未获取到正确的文件大小
            downloadCallBack.contentLengthError();
            return;
        }
        File dir = new File(locDirPath);
        if (!dir.exists()) {
            dir.mkdirs();
        }
        /* 获取本地文件完整路径 */
        final String locPath = getLocPath(path, locDirPath, fileTypes);
        /* 创建本地目标文件,并设置其大小为准备下载文件的总大小 */
        localFile = new File(locPath + ".tmp");
        RandomAccessFile accessFile = new RandomAccessFile(localFile, "rwd");
        accessFile.setLength(len);
        accessFile.close();
        /* 计算每条线程要下载的数据大小 */
        block = len % threadCount == 0 ? len / threadCount : len / threadCount
                + 1;
        downloadCallBack.totalSize(len);
        /* 启动线程下载文件 */
        for (int i = 0; i < threadCount; i++) {
            int threadId = i;
            long start = i * block;
            long end = start + block - 1;
            if (end >= len) {
                end = len - 1;
            }
            System.out.println("线程" + threadId + "开始");
            new DownloadThread(threadId, start, end, new Callback() {
                @Override
                public void complete(int threadId) {
                    completeNum++;
                    System.out.println("线程" + threadId + "完成"+completeNum+":"+threadCount);
                    if (completeNum == threadCount) {
                        // 全部下载完成
                        localFile.renameTo(new File(locPath));
                        downloadCallBack.complete(locPath);
                    }
                }

                @Override
                public void progress(int threadId, long size) {
                    downloadSize += size;
                    downloadCallBack.progress(len, downloadSize);
                }

                @Override
                public void error(int threadId) {
                    System.out.println("线程" + threadId + "error");
                    downloadCallBack.error();
                }
            }).start();
        }
    }

    public interface DownloadCallBack {
        /** 异常,结束下载,ContentLength<=0 */
        public void contentLengthError();

        public void totalSize(long total);

        public void complete(String path);

        public void progress(long total, long size);

        public void error();
    }

    int completeNum = 0;
    long downloadSize = 0;

    private String getLocPath(String path, String locDirPath,
            String... fileTypes) {
        String locPath;
        // 获取文件名
        String locName = OSUtil.toMD5(path);
        // 文件类型
        String fileType;
        if (fileTypes != null && fileTypes.length > 0) {
            fileType = fileTypes[0];
        } else {
            // 获取path中的文件格式后缀
            String[] s = path.split("\\.");
            fileType = s[s.length - 1];
        }
        if (fileType != null && fileType.trim().length() > 0) {
            // 如果path带有文件后缀,则使用其作为后缀
            locPath = locDirPath + File.separator + locName + "." + fileType;
        } else {
            locPath = locDirPath + File.separator + locName;
        }
        return locPath;
    }

    /**
     * 内部类: 文件下载线程类
     */
    private final class DownloadThread extends Thread {
        /* 线程 id */
        private int threadId;
        /* 线程下载结果回调 */
        private Callback callback;
        /* 开始下载的位置 */
        private long startPosition;
        /* 结束下载的位置 */
        private long endPosition;

        /**
         * 新建一个下载线程
         *
         * @param threadid
         *            线程 id
         */
        public DownloadThread(int threadId, long start, long end,
                Callback callback) {
            this.threadId = threadId;
            this.callback = callback;
            startPosition = start;
            endPosition = end;
        }

        @Override
        public void run() {
            RandomAccessFile accessFile = null;
            try {
                /* 设置从本地文件的什么位置开始写入数据 ,"rwd" 表示对文件具有读写删权限 */
                accessFile = new RandomAccessFile(localFile, "rwd");
                accessFile.seek(startPosition);
                HttpURLConnection conn = (HttpURLConnection) downloadUrl
                        .openConnection();
                conn.setRequestMethod("GET");
                conn.setReadTimeout(5 * 1000);
                /* 为 HTTP 设置 Range 属性,可以指定服务器返回数据的范围 */
                conn.setRequestProperty("Range", "bytes=" + startPosition + "-"
                        + endPosition);
                /* 将数据写往本地文件 */
                long size = writeTo(accessFile, conn);
                if (size >= endPosition - startPosition) {
                    callback.complete(threadId);
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    if (accessFile != null) {
                        accessFile.close();
                    }
                } catch (IOException ex) {
                    ex.printStackTrace();
                }
            }
        }

        /**
         * 将下载数据写往本地文件
         */
        private long writeTo(RandomAccessFile accessFile, HttpURLConnection conn) {
            InputStream is = null;
            long size = 0;
            try {
                is = conn.getInputStream();
                byte[] buffer = new byte[1024];
                int len = -1;
                while ((len = is.read(buffer)) != -1) {
                    accessFile.write(buffer, 0, len);
                    size += len;
                    callback.progress(threadId, len);
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    if (is != null) {
                        is.close();
                    }
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            }
            return size;
        }
    }

    interface Callback {
        public void complete(int threadId);

        public void progress(int threadId, long size);

        public void error(int threadId);
    }
}

posted @ 2017-02-22 16:52  IT老五  阅读(448)  评论(0编辑  收藏  举报