实现原理
(1)首先获得下载文件的长度,然后设置本地文件的长度。
(2)根据文件长度和线程数计算每条线程下载的数据长度和下载位置。
如:文件的长度为6M,线程数为3,那么,每条线程下载的数据长度为2M,每条线程开始下载的位置如下图所示:
?例如10M大小,使用3个线程来下载,
线程下载的数据长度 ? (10%3 == 0 ? 10/3:10/3+1) ,第1,2个线程下载长度是4M,第三个线程下载长度为2M
下载开始位置:线程id*每条线程下载的数据长度 = ?
下载结束位置:(线程id+1)*每条线程下载的数据长度-1=?

Activity代码

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    @SuppressLint("HandlerLeak")
    public static Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            int size = msg.getData().getInt("size");
//            progressbar.setProgress();
            float temp = (float) size / (float)progressbar.getMax();
            int progress = (int) (temp * 100);
            if (progress == 100) {
                Log.e("TAG", "handleMessage: " + "下载完成");
            }
            tv_result.setText("下载进度:" + progress + "%");
        }
    };
    private EditText ed;
    private Button btn_download;
    private static ProgressBar progressbar;
    private static TextView tv_result;
    private String urlstring;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
    }
 
    private void initView() {
        ed = (EditText) findViewById(R.id.ed);
        btn_download = (Button) findViewById(R.id.btn_download);
        progressbar = (ProgressBar) findViewById(R.id.progressbar);
        tv_result = (TextView) findViewById(R.id.tv_result);
 
 
        btn_download.setOnClickListener(this);
    }
 
 
    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_download:
                submit();
                download();
                break;
        }
    }
 
    //下载文件
    private void download() {
//获取sd卡路径
        String path = Environment.getExternalStorageDirectory() + "/morethreaddownload/";
        File file = new File(path);
        if (!file.exists()) {
//            如果文件目录不存在,就创建此文件夹
            file.mkdir();
        }
 
 
//      指定下载的大文件的名称
        String filename = "fulin.apk";
//        要下载的文件路径
        String filepath = path + filename;
//        线程的数量
        int threadcount = 5;
 
 
//  开启子线程下载文件(获取文件的长度)
        DownLoadTask task = new DownLoadTask(progressbar,urlstring,filepath,threadcount);
        task.start();
    }
    private void submit() {
//输入框中用户输入的网络路径
        urlstring = ed.getText().toString().trim();
        if (TextUtils.isEmpty(urlstring)) {
            Toast.makeText(this, "edString不能为空", Toast.LENGTH_SHORT).show();
            return;
        }
    }
 
}
DownLoadTask类

public class DownLoadTask extends Thread {
    //     网路文件的下载地址
    private String downloadUrl;
    //    保存文件的路径
    private String filePath;
    //    线程的数量
    private int threadcount;
//    进度条
    private ProgressBar  progressBar;
//    文件的大小
    public static int filesize;
    //    每个线程的下载量
    private int blockSize;
//构造方法
    public DownLoadTask(ProgressBar  progressBar, String downloadUrl, String filePath, int threadcount) {
        this.progressBar=progressBar;
        this.downloadUrl = downloadUrl;
        this.filePath = filePath;
        this.threadcount = threadcount;
    }
 
    @Override
    public void run() {
        super.run();
//        线程数组
        FileDownloadThread[] threads = new FileDownloadThread[threadcount];
        try {
            URL url = new URL(downloadUrl);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.connect();
            if (conn.getResponseCode() == 200) {
//                读取下载文件的总长度
                filesize = conn.getContentLength();
                if (filesize <= 0) {
                    Log.e(TAG, "run: " + "读取文件失败");
                    return;
                }
 
//                设置progressbar的最大值
                progressBar.setMax(filesize);
//
//              计算每条线程下载的数据长度
                blockSize = filesize % threadcount == 0 ? filesize / threadcount : filesize / threadcount + 1;
//sd卡里边指定的保存网络端下载下来的文件
                File file = new File(filePath);
//                启动每个线程,分别下载所分配的长度
                for (int i = 0; i < threadcount; i++) {
                    threads[i] = new FileDownloadThread(file,url,(i+1),blockSize);
                    threads[i].setName("Thread:" + i);
                    threads[i].start();
                }
//是否下载完成
                boolean isfinished = false;
//                下载的总长度
                int downloadAllsize = 0;
                while (!isfinished) {
                    isfinished = true;
                    downloadAllsize = 0;
                    for (int i = 0; i < threadcount; i++) {
                        downloadAllsize += threads[i].getDownloadlenght();
                        if (!threads[i].isCompleted()) {
                            isfinished = false;
                        }
                    }
//                    通知handler去更新
                    Message message = new Message();
                    message.getData().putInt("size", downloadAllsize);
                    MainActivity.handler.sendMessage(message);
                    Thread.sleep(1000);
                }
                Log.e(TAG, "run:下载的总大小: "+ downloadAllsize);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
class FileDownloadThread extends Thread {
    /**
     *  文件保存路径 
     */
    private File file;
    /**
     *  文件下载路径 (文件网址)
     */
    private URL downloadUrl;
    /**
     *  当前下载线程ID 
     */
    private int threadId;
    /**
     *  线程下载数据长度 
     */
    private int blockSize;
    private int downloadlenght;
    private boolean isCompleted;
 
    public FileDownloadThread(File file, URL downloadUrl, int threadId, int blockSize) {
        this.file = file;
        this.downloadUrl = downloadUrl;
        this.threadId = threadId;
        this.blockSize = blockSize;
    }
 
 
    @Override
    public void run() {
        super.run();
        BufferedInputStream  bis=null;
 
        RandomAccessFile raf=null;
 
 
 
        try {
            HttpURLConnection conn = (HttpURLConnection) downloadUrl.openConnection();
            conn.setAllowUserInteraction(true);
//         开始的位置
            int  startpos=blockSize*(threadId-1);
            int endpos=blockSize*threadId-1;
//            设置当前线程下载的开始点和结束点
            conn.setRequestProperty("Range","bytes="+startpos+"-"+endpos);
            byte[] buffer=new byte[1024];
            bis=new BufferedInputStream(conn.getInputStream());
            raf=new RandomAccessFile(file,"rwd");
            raf.seek(startpos);
            int len=0;
            while((len=bis.read(buffer,0,1024))!=-1){
                raf.write(buffer,0,len);
                downloadlenght+=len;
            }
            isCompleted=true;
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
//每个线程下载的数据长度
    public int getDownloadlenght() {
        return downloadlenght;
    }
//返回线程是否下载完成
    public boolean isCompleted() {
        return isCompleted;
    }
}

FileDownloadThread类

class FileDownloadThread extends Thread {
    /**
     *  文件保存路径 
     */
    private File file;
    /**
     *  文件下载路径 (文件网址)
     */
    private URL downloadUrl;
    /**
     *  当前下载线程ID 
     */
    private int threadId;
    /**
     *  线程下载数据长度 
     */
    private int blockSize;
    private int downloadlenght;
    private boolean isCompleted;
 
    public FileDownloadThread(File file, URL downloadUrl, int threadId, int blockSize) {
        this.file = file;
        this.downloadUrl = downloadUrl;
        this.threadId = threadId;
        this.blockSize = blockSize;
    }
 
 
    @Override
    public void run() {
        super.run();
        BufferedInputStream  bis=null;
 
        RandomAccessFile raf=null;
 
 
 
        try {
            HttpURLConnection conn = (HttpURLConnection) downloadUrl.openConnection();
            conn.setAllowUserInteraction(true);
//         开始的位置
            int  startpos=blockSize*(threadId-1);
            int endpos=blockSize*threadId-1;
//            设置当前线程下载的开始点和结束点
            conn.setRequestProperty("Range","bytes="+startpos+"-"+endpos);
            byte[] buffer=new byte[1024];
            bis=new BufferedInputStream(conn.getInputStream());
            raf=new RandomAccessFile(file,"rwd");
            raf.seek(startpos);
            int len=0;
            while((len=bis.read(buffer,0,1024))!=-1){
                raf.write(buffer,0,len);
                downloadlenght+=len;
            }
            isCompleted=true;
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
//每个线程下载的数据长度
    public int getDownloadlenght() {
        return downloadlenght;
    }
//返回线程是否下载完成
    public boolean isCompleted() {
        return isCompleted;
    }
}

详细配置信息可以参考我写的这篇文章:http://blog.ncmem.com/wordpress/2019/08/28/net%e6%96%87%e4%bb%b6%e6%89%b9%e9%87%8f%e4%b8%8b%e8%bd%bd/

web大文件下载+断点续传的更多相关文章

  1. java web 大文件下载

    泽优大文件下载产品测试 泽优大文件下载控件down2,基于php开发环境测试. 开发环境:HBuilder 服务器:wamp64 数据库:mysql 可视化数据库编辑工具:Navicat Premiu ...

  2. php大文件下载+断点续传

    如果我们的网站提供文件下载的服务,那么通常我们都希望下载可以断点续传(Resumable Download),也就是说用户可以暂停下载,并在未来的某个时间从暂停处继续下载,而不必重新下载整个文件. 通 ...

  3. B/S大文件下载+断点续传

    1.先将 webuploader-0.1.5.zip 这个文件下载下来:https://github.com/fex-team/webuploader/releases  根据个人的需求放置自己需要的 ...

  4. java大文件下载+断点续传

    java两台服务器之间,大文件上传(续传),采用了Socket通信机制以及JavaIO流两个技术点,具体思路如下: 实现思路:1.服:利用ServerSocket搭建服务器,开启相应端口,进行长连接操 ...

  5. jsp大文件下载+断点续传

    以多线程.断点续传方式下载文件,经常出现下载下来的文件大小和服务端一致,但是却无法正常打开的现象,搞了很久,贴下我的实现方式,请各位多多指教思路:1.将下载文件的处理放在自定义的线程类中,每下载一个文 ...

  6. Web大文件下载控件(down2)-示例更新-Xproer.HttpDownloader

    版权所有 2009-2016 荆门泽优软件有限公司 保留所有权利 官方网站:http://www.ncmem.com/ 产品首页:http://www.ncmem.com/webapp/down2/i ...

  7. Web大文件下载控件更新-Xproer.HttpDownloader

    资源下载:cab安装包(x86),cab安装包(x64),xpi安装包,crx安装包,nat安装包,exe安装包,开发文档,根证书,VC库,   更新时间:2016-08-19 版本号:1,2,56, ...

  8. web大文件断点续传

    1,项目调研 因为需要研究下断点上传的问题.找了很久终于找到一个比较好的项目. 在GoogleCode上面,代码弄下来超级不方便,还是配置hosts才好,把代码重新上传到了github上面. http ...

  9. Web大文件(夹)上传(断点续传)控件-Xproer.HttpUploader6

    版权所有 2009-2017荆门泽优软件有限公司 保留所有权利 官方网站:http://www.ncmem.com/ 产品首页:http://www.ncmem.com/webapp/up6.2/in ...

随机推荐

  1. Java第一周总结

    通过两周的Java学习最深刻的体会就是Java好像要比C要简单一些. 然后这两周我学习到了很多东西,李老师第一次上课就给我们介绍了Java的发展历程,同时还有Java工具jdk的发展历程. Java语 ...

  2. cdh平台问题

    问题背景:内容的不懂之处,可以私信博主.友好交流使用.主要针对的问题种类有:网络桥接报错.网卡文件问题(该问题主要看你的安装脚本文件里面写的是否和主机对应,也是运行环境的问题).scm表中没有节点信息 ...

  3. Nginx 的root和 alias

    nginx是通过alias设置虚拟目录,在nginx的配置中,alias目录和root目录是有区别的:1)alias指定的目录是准确的,即location匹配访问的path目录下的文件直接是在alia ...

  4. 解决MySql忘记密码

    描述:忘记了mysql的登录密码,无法登录的情况下该怎么办? 环境:CentOS 7,数据库:mysql 5.7 1.停止数据库(先查看mysql服务是否运行) # ps -ef | -i grep ...

  5. 为什么 Java 线程没有 Running 状态?

    Java虚拟机层面所暴露给我们的状态,与操作系统底层的线程状态是两个不同层面的事.具体而言,这里说的 Java 线程状态均来自于 Thread 类下的 State 这一内部枚举类中所定义的状态: 什么 ...

  6. es6 js数组常用方法

    一:会改变自身的方法 1.array.push(element1, ...elementN) 添加一个或多个元素到数组的末尾,并返回数组新的长度 2.array.unshift(element1, . ...

  7. linux下的变量规则

    1.变量名可以由字母.数字和下划线组成,但是不能以数字开头: 2.变量在命名时,默认情况下为字符型: 3.为变量赋值时,等号两边不能有空格: 4.如果要增加变量的值,可以进行变量的叠加,不过需要将变量 ...

  8. python学习第五十三天configParser模块的使用

    configParser 模块用于生成和修改常见配置文档,python 3.x为configParser,配置软件的常见配置格式 模块的用法 import configparser config=co ...

  9. offsetWidth clientWidth scrollWidth 的区别

    了解 offsetWidth clientWidth scrollWidth 的区别 最近需要清除区分开元素的width,height及相应的坐标等,当前这篇用来区分offsetWidth clien ...

  10. 04-Django-templates

    # 模板系统 - 模板:一组相同或者相似的页面,在需要个性化的地方进行留白,需要的时候只是用数据填充就可以使用 - 步骤: 1. 在settings中进行设置:TEMPLATES 2. 在tmeplt ...