Java 实现多线程文件下载(HTTP)

实现原理:

用httpConnection.setRequestProperty("RANGE", "bytes= xxx-xxx");获取指定的数据块

用RandomAccessFile实现文件随机访问,写入指定数据块到文件.

关键事项:获取远程文件大小,根据文件大小确定下载线程个数(可固定线程数,也可固定每个线程下载数据块大小,用户自己决定).

package org.hundred.multithreads;

import java.io.File;

import java.io.IOException;

import java.io.RandomAccessFile;

import java.net.HttpURLConnection;

import java.net.URL;

/**

 * 文件下载管理类

 */

public class DownLoadManager {

    /**

     * 每个线程下载的字节数

     */

    static final long unitSize = 1000 * 1024;

    /**

     * 启动多个线程下载文件

     */

    public void doDownload(String remoteFileUrl)

            throws IOException {

        String fileName = new URL(remoteFileUrl).getFile();

        fileName = fileName.substring(fileName.lastIndexOf("/")+1,fileName.length()).replace("%20", " ");

        long fileSize = this.getRemoteFileSize(remoteFileUrl);

        if(fileSize == 0){

            return;

        }

        this.createFile(fileName, fileSize);

        long threadCount = fileSize / unitSize;

        System.out.println("共启动 "

                + (fileSize % unitSize == 0 ? threadCount : threadCount + 1)

                + " 个线程");

        long offset = 0;

        if (fileSize <= unitSize) {// 如果远程文件尺寸小于等于unitSize

            DownloadThread downloadThread = new DownloadThread(remoteFileUrl,

                    fileName, offset, fileSize);

            downloadThread.start();

        } else {// 如果远程文件尺寸大于unitSize

            for (int i = 1; i <= threadCount; i++) {

                DownloadThread downloadThread = new DownloadThread(

                        remoteFileUrl, fileName, offset, unitSize);

                downloadThread.start();

                offset = offset + unitSize;

            }

            if (fileSize % unitSize != 0) {// 如果不能整除,则需要再创建一个线程下载剩余字节

                DownloadThread downloadThread = new DownloadThread(

                        remoteFileUrl, fileName, offset, fileSize

                                - unitSize * threadCount);

                downloadThread.start();

            }

        }

    }

    /**

     * 获取远程文件尺寸

     */

    private long getRemoteFileSize(String remoteFileUrl) throws IOException {

        long fileSize = 0;

        HttpURLConnection httpConnection = (HttpURLConnection) new URL(

                remoteFileUrl).openConnection();

        httpConnection.setRequestMethod("HEAD");

        int responseCode = httpConnection.getResponseCode();

        if(responseCode >= 400){

            System.out.println("Web服务器响应错误!");

            return 0;

        }

        String sHeader;

        for(int i=1;;i++){

            sHeader = httpConnection.getHeaderFieldKey(i);

            if(sHeader != null && sHeader.equals("Content-Length")){

                System.out.println("文件大小ContentLength:"+httpConnection.getContentLength());

                fileSize = Long.parseLong(httpConnection.getHeaderField(sHeader));

                break;

            }

        }

        return fileSize;

    }

    /**

     * 创建指定大小的文件

     */

    private void createFile(String fileName, long fileSize) throws IOException {

        File newFile = new File(fileName);

        RandomAccessFile raf = new RandomAccessFile(newFile, "rw");

        raf.setLength(fileSize);

        raf.close();

    }

}

 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

package org.angus.multithreads;

import java.io.BufferedInputStream;

import java.io.File;

import java.io.IOException;

import java.io.RandomAccessFile;

import java.net.HttpURLConnection;

import java.net.URL;

/**

 * 负责文件下载的类

 */

public class DownloadThread extends Thread {

    /**

     * 待下载的文件

     */

    private String url = null;

    /**

     * 本地文件名

     */

    private String fileName = null;

    /**

     * 偏移量

     */

    private long offset = 0;

    /**

     * 分配给本线程的下载字节数

     */

    private long length = 0;

    /**

     * @param url 下载文件地址

     * @param fileName 另存文件名

     * @param offset 本线程下载偏移量

     * @param length 本线程下载长度

     *

     * @author Angus.wang

     * */

    public DownloadThread(String url, String file, long offset, long length) {

        this.url = url;

        this.fileName = file;

        this.offset = offset;

        this.length = length;

        System.out.println("偏移量=" + offset + ";字节数=" + length);

    }

    public void run() {

        try {

            HttpURLConnection httpConnection = (HttpURLConnection) new URL(

                    this.url).openConnection();

            httpConnection.setRequestMethod("GET");

            httpConnection.setRequestProperty("RANGE", "bytes=" + this.offset

                    + "-" + (this.offset + this.length - 1));

            System.out.println("RANGE bytes=" + this.offset + "-" + (this.offset + this.length - 1));

            BufferedInputStream bis = new BufferedInputStream(httpConnection

                    .getInputStream());

            byte[] buff = new byte[1024];

            int bytesRead;

            File newFile = new File(fileName);

            RandomAccessFile raf = new RandomAccessFile(newFile, "rw");

            while ((bytesRead = bis.read(buff, 0, buff.length)) != -1) {

                raf.seek(this.offset);

                raf.write(buff, 0, bytesRead);

                this.offset = this.offset + bytesRead;

            }

            raf.close();

            bis.close();

        } catch (IOException ioe) {

            ioe.printStackTrace();

        }

    }

}

 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

package org.angus.multithreads;

/**

 * 多线程文件下载测试

 * */

public class FileDownloadTest {

    /**

     * @param args

     */

    public static void main(String[] args) {

        try{

            String remoteFileUrl = "http://dl.maxthon.cn/cn/mx2/mx_2.5.1.4751cn.exe";

            DownLoadManager downLoadManager = new DownLoadManager();

            downLoadManager.doDownload(remoteFileUrl);

        }catch(Exception e){

            e.printStackTrace();

        }

    }

}
时间: 2024-10-19 22:28:09

Java 实现多线程文件下载(HTTP)的相关文章

Java多线程文件下载

以下代码有点问题,会发生阻塞,还不知道啥问题: package com.test.service; import java.io.File; import java.io.InputStream; import java.io.RandomAccessFile; import java.net.HttpURLConnection; import java.net.URL; import java.util.concurrent.CountDownLatch; import org.slf4j.L

Android多线程文件下载器

本应用实现的是输入文件的网络的地址,点击按钮开始下载,下载过程中有进度条和后面的文本提示进度, 下载过程中按钮不可点击,防止重复的下载,下载完毕后会进行Toast的提示显示, 并且回复按钮的可点击性,进度条也会清空,当然如果下载中途结束应用进程就会进行进度的保存, 下次下载同样的文件时就会从进度记录进行下载,节省流量和时间 应用需要的应用权限: 访问网络权限 <uses-permission android:name="android.permission.INTERNET"/&

java实现大文件下载(http方式)

java实现大文件下载,基于http方式,控件神马的就不说了. 思路:下载文件无非要读取文件然后写文件,主要这两个步骤,主要难点: 1.读文件,就是硬盘到内存的过程,由于jdk内存限制,不能读的太大. 2.写文件,就是响应到浏览器端的过程,http协议是短链接,如果写文件太慢,时间过久,会造成浏览器死掉. 知识点: 1.org.apache.http.impl.client.CloseableHttpClient  模拟httpClient客户端发送http请求,可以控制到请求文件的字节位置.

Java基础--多线程的方方面面

1,什么是线程?线程和进程的区别是什么? 2,什么是多线程?为什么设计多线程? 3,Java种多线程的实现方式是什么?有什么区别? 4,线程的状态控制有哪些方法? 5,线程安全.死锁和生产者--消费者 6,线程的优化有哪些方法? 1,什么是线程?线程和进程的区别是什么? 线程是程序执行的最小单元. 区别: 进程是操作系统进行资源处理和分配的最小单位,而一个进程可以包含多个线程,并共享进程的资源. 2,什么是多线程?为什么设计多线程? 介绍之前,我们需要理解并行和并发的定义: 并行:同一个时刻有多

Java中都通用文件下载(ContentType、文件头、response、out四步骤)

Java中都通用文件下载(ContentType.文件头.response.out四步骤) 新浪微博:IT国子监(记得关注噢) http://weibo.com/itguozijian   我们就直接切入主题啦,文件下载只需要四步: 1.设置文件ContentType类型 2.设置文件头 3.通过response获取ServletOutputStream对象(out) 4.写到输出流(out)中 下载代码: 这里我使用的是SpringMVC,不过它在这里的唯一用途就是用来获取ServletCon

Java的多线程编程模型5--从AtomicInteger开始

Java的多线程编程模型5--从AtomicInteger开始 2011-06-23 20:50 11393人阅读 评论(9) 收藏 举报 java多线程编程jniinteger测试 AtomicInteger,一个提供原子操作的Integer的类.在Java语言中,++i和i++操作并不是线程安全的,在使用的时候,不可避免的会用到synchronized关键字.而AtomicInteger则通过一种线程安全的加减操作接口. 来看AtomicInteger提供的接口. //获取当前的值 publ

java实现多线程下载

本篇博客可认为是对 使用java实现http多线程下载 一文的再次解读. 首先,从宏观来说 java实现多线程下载这个功能的实现由以下几部分组成: 1 建立多个线程去分别下载文件的一部分. 2 将多个线程下载的文件(还在内存中),写入硬盘中的一个文件. 3 断点续传 GET /Path/FileName HTTP/1.0 Host: www.server.com:80 Accept: */* User-Agent: GeneralDownloadApplication Connection: c

从jvm的角度来看java的多线程

最近在学习jvm,发现随着对虚拟机底层的了解,对java的多线程也有了全新的认识,原来一个小小的synchronized关键字里别有洞天.决定把自己关于java多线程的所学整理成一篇文章,从最基础的为什么使用多线程,一直深入讲解到jvm底层的锁实现. 多线程的目的 为什么要使用多线程?可以简单的分两个方面来说: 在多个cpu核心下,多线程的好处是显而易见的,不然多个cpu核心只跑一个线程其他的核心就都浪费了: 即便不考虑多核心,在单核下,多线程也是有意义的,因为在一些操作,比如IO操作阻塞的时候

Java的多线程机制系列:不得不提的volatile及指令重排序(happen-before)

一.不得不提的volatile volatile是个很老的关键字,几乎伴随着JDK的诞生而诞生,我们都知道这个关键字,但又不太清楚什么时候会使用它:我们在JDK及开源框架中随处可见这个关键字,但并发专家又往往建议我们远离它.比如Thread这个很基础的类,其中很重要的线程状态字段,就是用volatile来修饰,见代码 /* Java thread status for tools, * initialized to indicate thread 'not yet started' */   p