精华内容
下载资源
问答
  • 多线程断点续传代码

    2016-09-23 12:51:08
    多线程断点续传代码
  • Broken-point 多线程断点续传代码 使用MVP+handler
  • Java多线程断点续传代码详解

    多线程断点续传下载的几处关键步骤:

    1.创建临时文件空间并计算每个线程需要下载的部分size

    2.每个线程需要下载的开始和结束startIndex和endIndexe,使用获取的流的长度除以size就得到了每个线程需要下载的部分大小,但是如果除不尽的都留给最后一个线程来下载,

    3.保证断点续传的机制,使用的是临时文件来存放每个线程下载的进度,每次开始下载前先判断一下该临时文件是否存在,以及记录的对应的线程下载记录,根据记录开始本次下载,判断每个线程是否下载完毕,若果都下载完毕则通过循环删除临时文件,为避免并发操作应该对删除临时文件加锁。

    4.具体下载过程,再次获取连接,根据206的状态码获取读取流,然后就是Java中的io读写的过程了,需要用到的RandomAccessFile来实现临时文件的创建和写入,简单说是因为每个线程请求的部分不同,因而写入的时候也不是从零开始写的,而RandomAccessFile的seek方法,支持写入时的定位,也就是 在指定位置写入文件,然后几个部分拼凑起来就是完成的要下载的文件了。

    5.此外还有需要注意的细节,比如请求网络的代码要放在子线程中来实现,即具体下载过程部分的代码。

    以下是具体实现代码,代码中包含详细注解:

    package com.nocol.mutildownload;
    
    import java.io.BufferedReader;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.io.RandomAccessFile;
    import java.net.HttpURLConnection;
    import java.net.URL;
    
    /**
     * @author lxp
     *
     * @TODO
     * 
     */
    
    public class MutilDownLoad {
    	
    	//获取文件下载路径
    	public static String path = "http://192.168.56.1:8080/haozip.exe";
    	// 定义线程个数
    	public static int ThreadCount = 3;
    
    	// ===定义正在运行的线程===
    	public static int RunningThread;
    
    	public static void main(String[] args) throws Exception {
    		// 连接服务器,获取下载的文件长度
    		// 创建URL对象
    		URL url = new URL(path);
    		// 获取连接对象
    		HttpURLConnection conn = (HttpURLConnection) url.openConnection();
    		// 设置请求数据的方式
    		conn.setRequestMethod("GET");
    		// 设置请求超时时间
    		conn.setReadTimeout(5000);
    		// 获取服务器状态码
    		int code = conn.getResponseCode();
    		if (code == 200) {
    			// 获取文件实际长度
    			int lenth = conn.getContentLength();
    
    			// ===将线程个数赋值给正在运行的线程====
    			RunningThread = ThreadCount;
    
    			// 创建要下载文件的文件空间
    			RandomAccessFile raf = new RandomAccessFile(getName(path), "rw");
    			// 指定该空间的长度(和要下载的文件的长度相同)
    			raf.setLength(lenth);
    			raf.close();
    			// 设置每个线程要下载的文件长度
    			int blockSize = lenth / ThreadCount;
    			for (int ThreadId = 1; ThreadId <= ThreadCount; ThreadId++) {
    				// 设置没每个线程下载的开始位置
    				int startIndex = (ThreadId - 1) * blockSize;
    				int endIndex = startIndex + (blockSize - 1);
    				if (ThreadId == ThreadCount) {
    					endIndex = lenth;
    				}
    
    				System.out.println(ThreadId + "理论下载的大小:" + startIndex + "-----" + endIndex);
    
    				// 开启线程下载
    				DownLoadThread downLoadThread=new DownLoadThread(startIndex, endIndex, ThreadId);
    				downLoadThread.start();
    			}
    		}
    	}
    
    	// 创建线程下载文件
    	public static class DownLoadThread extends Thread {//静态内部类
    
    		private int startIndex;
    		private int endIndex;
    		private int ThreadId;
    
    		public DownLoadThread(int startIndex, int endIndex, int ThreadId) {
    			this.endIndex = endIndex;
    			this.startIndex = startIndex;
    			this.ThreadId = ThreadId;
    		}
    
    		@Override
    		public void run() {
    			try {
    				// 创建URL对象
    				URL url = new URL(path);
    				// 获取连接对象
    				HttpURLConnection conn = (HttpURLConnection) url.openConnection();
    				// 设置请求数据的方式
    				conn.setRequestMethod("GET");
    				// 设置请求超时时间
    				conn.setReadTimeout(5000);
    
    				// ==将记录线程位置 的文件封装成File对象
    				File file = new File(ThreadId + ".txt");
    				// ==下载前判断是否存在保存线程下载位置的文件来判断是否要进行断点续传
    				if (file.exists()) {
    					FileInputStream fis = new FileInputStream(file);
    					BufferedReader br = new BufferedReader(new InputStreamReader(fis));
    					// 获取线程下载的最后的位置
    					String Lastpostion = br.readLine();
    					// 装换为int类型
    					int LastPositionn = Integer.parseInt(Lastpostion);
    					// 将该位置赋值给起始位置
    					startIndex = LastPositionn;
    					// 释放资源
    					fis.close();
    
    					System.out.println(ThreadId + "实际下载的大小:" + startIndex + "-----" + endIndex);
    				}
    
    				// 很重要:请求服务器下载部分的文件的指定的位置(请求头信息)
    				conn.setRequestProperty("Range", "bytes=" + startIndex + "-" + endIndex);
    				// 获取服务器状态码
    				int code = conn.getResponseCode();
    				if (code == 206) {// 状态码=206表示请求部分资源成功
    					// 获取输入流对象
    					InputStream in = conn.getInputStream();
    					// 创建随机访问文件对象
    					RandomAccessFile raf = new RandomAccessFile(MutilDownLoad.getName(path), "rw");
    					// 指定线程下载文件的开始位置
    					raf.seek(startIndex);
    
    					// 将读取的文件写入创建的文件空间
    					int len = 0;
    					byte[] bys = new byte[1024 * 1024];
    
    					// ===定义当前线程下载的大小
    					int total = 0;
    
    					while ((len = in.read(bys)) != -1) {
    						raf.write(bys, 0, len);
    
    						// ==total即为每个线程从各自其实位置开始下载的文件大小
    						total += len;
    						// ==为实现断点续传,获取当前线程下载的位置
    						int CurrentThreadPosition = startIndex + total;
    						// ==将当前线程下载的位置记录下来(.txt文件),定义随机访问而文件对象
    						// =="rwd" 打开以便读取和写入,对于 "rw",还要求对文件内容的每个更新都同步写入到底层存储设备。
    						RandomAccessFile raff = new RandomAccessFile(ThreadId + ".txt", "rwd");
    						raff.write(String.valueOf(CurrentThreadPosition).getBytes());
    						raff.close();
    					}
    					// 释放资源
    					in.close();
    					raf.close();
    
    					System.out.println("线程" + ThreadId + "下载完成!");
    
    					// ===在每个线程下载完毕后将每个线程的".txt"文件删除,为避免并发操作,为删文件操作加锁====
    					synchronized (DownLoadThread.class) {
    						RunningThread--;// 线程下载完成后将不在运行,运行中的线程个数相应减少
    						if (RunningThread == 0) {// 当RunningThread个数为0时说明文件全部下载完成
    							for (int i = 1; i <= ThreadCount; i++) {
    								File deleteFile = new File(i + ".txt");
    								deleteFile.delete();
    							}
    						}
    					}
    				}
    			} catch (Exception e) {
    				e.printStackTrace();
    			}
    		}
    	}
    	
    	//获取源文件的文件名
    	public static String getName(String path){
    		//http://192.168.56.1:8080/haozip.exe
    		//获取"/"最后一次出现的索引
    		int index=path.lastIndexOf("/");
    		String name=path.substring(index+1);
    		return name;
    	}
    }
    


    展开全文
  • 主要为大家详细介绍了Android多线程断点续传下载功能的实现代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
  • 学习了多线程下载,而且可以断点续传的逻辑,线程数量可以自己选择,但是线程数量过多手机就承受不起,导致闪退,好在有断点续传。 步骤写在了代码的注释里。大概就是获取服务器文件的大小,在本地新建一个相同大小...
  • 支持HTTP&FTP多线程断点续传代码
  • VC++多线程断点续传下载代码!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  • itcast传智播客android多线程断点续传下载代码 可运行的
  • 点对点多线程断点续传软件源代码,里面有2个例子
  • 多线程断点续传要点

    2017-05-27 17:23:53
    定义首先我们要明白多线程断点续传是什么,我看到有人理解为开了个线程去下载支持断点续的文件就是多线程断点续传了,其实不然.所谓多线程断点续传是指开多个线程去分段下同一个文件.

    定义

    首先我们要明白多线程断点续传是什么,我看到有人理解为开了个线程去下载支持断点续传的文件就是多线程断点续传了,其实不然.所谓多线程断点续传是指开多个线程去分段下同一个文件.

    作用

    断点续传:假如下载大文件不支持断点续传,用户网络一不稳定,或者切换网络,只要一失败又得从头开始下载

    多线程断点续传:如有个1200KB的文件,服务器下发单个连接每秒下载速度上限为200KB, 客户端网络下载速度为600KB/S.在这种场景下,假如我们这里不考虑其他因素,单线程去下载最少6s. 开启3个线程去分段下载这个文件2s就可以下完.

    断点续传支持

    当然一般我们的项目都是已经知道服务器给的文件是否支持,所以大多时候不需要去判断
    

    方式一(推荐)
    根据connection.getHeaderField(“Accept-Ranges”)的值来判断

    代码片段

                HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
                connection.setRequestMethod("GET");
                connection.setConnectTimeout(Constants.CONNECT_TIME);
                connection.setReadTimeout(Constants.READ_TIME);
                int responseCode = connection.getResponseCode();
                int contentLength = connection.getContentLength();
                boolean isSupportRange = false;
                if (responseCode == HttpURLConnection.HTTP_OK) {
                    String ranges = connection.getHeaderField("Accept-Ranges");
                    if ("bytes".equals(ranges)) {
                        isSupportRange = true;
                    }
                }

    方式一(有风险)
    设置range头connection.setRequestProperty(“Range”, “bytes=0-” + Integer.MAX_VALUE);然后根据
    if (responseCode == HttpURLConnection.HTTP_PARTIAL)来判断,当文件大概达到4G的时候,Integer.MAX_VALUE的值是完全不够的,所以会导致问题.

    代码片段

                HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
                connection.setRequestMethod("GET");
                connection.setRequestProperty("Range", "bytes=0-" + Integer.MAX_VALUE);
                connection.setConnectTimeout(Constants.CONNECT_TIME);
                connection.setReadTimeout(Constants.READ_TIME);
                int responseCode = connection.getResponseCode();
                int contentLength = connection.getContentLength();
                boolean isSupportRange = false;
                if (responseCode == HttpURLConnection.HTTP_PARTIAL) {  
                        isSupportRange = true;
                }
                connection.disconnect();

    下载

    下载需要注意分段的position问题,要明白是从0开始的.
    比如600byte分3段(0~199,200~399,400~599)

    代码片段

            long perSize = downloadEntry.totalLength / MAX_DOWNLOAD_THREAD;
            long startPosition;
            long endPosition;
            if (downloadEntry.progressMaps == null) {
                downloadEntry.progressMaps = new HashMap<>();
                for (int i = 0; i < MAX_DOWNLOAD_THREAD; i++) {
                    downloadEntry.progressMaps.put(i, 0);
                }
            }
            downloadThread = new DownloadThread[MAX_DOWNLOAD_THREAD];
            for (int i = 0; i < MAX_DOWNLOAD_THREAD; i++) {
                startPosition = i * perSize + downloadEntry.progressMaps.get(i);
                if (i == MAX_DOWNLOAD_THREAD - 1) {
                    endPosition = downloadEntry.totalLength - 1;
                } else {
                    endPosition = (i + 1) * perSize - 1;
                }
                if (startPosition < endPosition) {
                    downloadThread[i] = new DownloadThread(downloadEntry.url, i, startPosition, endPosition, this);
                    executorService.execute(downloadThread[i]);
                }
    
            }
    

    代码片段

                HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
                connection.setRequestMethod("GET");
                connection.setRequestProperty("Range", "bytes=" + startPostion + "-" + endPosition);
                connection.setConnectTimeout(Constants.CONNECT_TIME);
                connection.setReadTimeout(Constants.READ_TIME);
                int responseCode = connection.getResponseCode();
                RandomAccessFile raf;
                int length;
                byte[] bytes = new byte[1024];
                InputStream is;
                File file = new File(path);
                if (responseCode == HttpURLConnection.HTTP_PARTIAL) {
                    raf = new RandomAccessFile(file, "rw");
                    raf.seek(startPostion);
                    is = connection.getInputStream();
                    while ((length = is.read(bytes)) != -1) {
                        if (isPause) {
                            break;
                        }
                        raf.write(bytes, 0, length);
                        if (!file.exists()) {
                            throw new FileNotFoundException("Invalid file path");
                        }
                        downloadListener.onProgressChanged(index, length);
                    }
                    raf.close();
                    is.close();
                    connection.disconnect();

    拓展

    了解了这些,你完全可以自己撸一个多线程断点续传框架

    展开全文
  • 自己为了课程设计写的程序,基于java的多线程断点续传程序
  • java socket 多线程 断点续传实现

    热门讨论 2014-01-07 11:32:22
    最近研究了一下socket套接字 实现java多线程 断点续传文件 在网上查找了很多例子 然后学习了大家的方法 最后利用网上的例子 自己整合了一份多线程 断点续传文件的代码 并且能实现客户端发送完毕之后 接收服务器端的...
  • 本文实例为大家分享了android实现多线程断点续传的具体代码,供大家参考,具体内容如下 布局: <?xml version=1.0 encoding=utf-8?> <RelativeLayout xmlns:android=...
  •  网上有许多的多线程断点续传操作,但总是写的很云里雾里,或者写的比较坑长。由于这几个月要负责公司的在线升级项目,所以正好顺便写了一下 代码如下: using System; using System.Collections.Generic; using ...
  • android多线程断点续传下载源代码,有需要的朋友可以下载
  • 多线程断点续传

    2015-03-25 11:34:53
    多线程实现的断点续传功能,具体可看代码中的注释
  • Android多线程断点续传下载+在线播放音乐,我想很多小白都想要。 Android多线程断点续传下载+在线播放音乐,我想很多小白都想要。
  • 多线程断点续传下载

    多线程断点续传下载

    多线程原理

    • 服务器给每条线程分配的时间片段相同,并且平均分配带宽,所以客户端开启的线程越多,就能抢占到更多的服务器资源。

    确定每条线程下载多少数据

    • 发送请求到服务器
            String path = "http://192.168.13.13/pal.exe";
            URL url =new URL(path);
            HttpURLConnection conn =(HttpURLConnection)url.openConnection();
            conn.setReadTimeOut(5000);
            conn.setConnectTimeout(5000);
            conn.setRequestMethod("GET");
    • 获取文件总长度,然后创建长度一致的临时文件
            if(conn.getResponseCode()==200){
            //获取服务器流中数据的长度
            int length = conn.getContentLength();
            //创建一个临时文件并存储下载数据
            RandomAccessFile raf = new RandomAccessFile(getFileName(path),"rwd");
            //设置临时文件的大小
            raf.setLength(length);
            raf.close();
            //计算每个线程下载多少数据
            int blockSize = length / THREAD_COUNT;

    计算每条线程下载数据的开始位置和结束位置

    • 注意最后一个线程的结束
            for(int i=0;i<THREAD_COUNT;i++){
            //计算每个线程下载数据的开始位置和结束位置
                int startIndex = i* blockSize;
                int endIndex = (i +1)* blockSize - 1;
                if(id == THREAD_COUNT-1){
                    endIndex = length-1;
                }
    
                //开启线程,按照计算出来的开始结束位置开始下载数据
                new DownLoadThread(startIndex, endIndex, id).start();
                }
             }

    再次发送请求至下载地址

    • 请求开始位置至结束位置的数据
            String path = "http://192.168.1.102:8080/editplus.exe";
    
            URL url = new URL(path);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setReadTimeout(5000);
            conn.setConnectTimeout(5000);
            conn.setRequestMethod("GET");
    
            //向服务器请求部分数据设置
            conn.setRequestProperty("Range","bytes="+startIndex+"-"+endIndex);
            conn.connect();
    • 下载请求到的数据,放置到临时文件中
            if(conn.getResponseCode()==206){
            InputStream is =conn.getInputStream();
            RandomAccessFile raf = new RandomAccessFile(getFileName(path),"rwd");
            //指定从哪个位置开始存放数据
            raf.seek(startIndex);
                byte[] b = new byte[1024];
                int len;
                while((len = is.read(b)) != -1){
                    raf.write(b, 0, len);
                }
                raf.close();
            }

    带断点续传功能的多线程下载

    • 定义一个int变量记录每条线程下载的数据总长度,然后加上该线程的下载开始位置,得到的结果就是下次下载时,该线程的开始位置,把得到的结果存入缓存文件
            //用来记录当前线程的下载长度
            int total = 0;
            while((len=is.read(b))!=-1){
            raf.write(b,0,len);
            total+=len;
            //每次系在都把最新的下载位置写入缓存文本文件
            File file =new File(threadId+".txt");
            RandomAccessFile progressRaf=new RandomAccessFile(progressFile,"rwd");
            progressRaf.write((startIndex+total+"").getBytes());
            progressRaf.close();
            }
    • 下次开始时,先读取缓存文件中的值,得到的值就是线程新的开始位置
            FileInputStream fis = new FileInputStream(file);
            BufferedRead br =new BufferedRead(new InputStreamRead(fis));
            String text = br.readLine();
            int newStartIndex = Ingeter.parseInt(text);
            //把读取到的值作为新的开始位置
            startIndex = newStartIndex;
            fis.cloes();
    • 三条线程都下载完成以后,删除缓存文件
            finalThread++;
            synchronized(path){    
            if(RUNNING_THREAD == 0){
                for(int i = 0; i <= 3; i++){
                    File f = new File(i + ".txt");
                    f.delete();
                    }
                }        
            }        

    手机版的断点续传多线程下载器

    • 把刚才的代码直接粘贴过来就能用,记得在访问文件时的路径要改成Android的目录,添加访问网络和外部存储的路径

    用进度条显示下载进度

    • 拿到下载文件总长度时,设置进度条的最大值
            //设置进度条的最大值
            pb.setMax(length);
    • 进度条需要显示三条线程的整体下载进度,所以三条线程每下载一次,就要把新下载的长度加入进度条
      • 定义一个int全局变量,记录三条线程的总下载长度
                int progress;
    • 刷新进度条
                while((len = is.read(b)) != -1){
                    raf.write(b, 0, len);      
                    //把当前线程本次下载的长度加到进度条里
                    progress += len;
                    pb.setProgress(progress);
                }
    • 每次断点下载时,从新的开始位置开始下载,进度条也要从新的位置开始显示,在读取缓存文件获取新的下载开始位置时,也要处理进度条进度
            FileInputStream fis = new FileInputStream(file);
            BufferedReader br = new BufferedReader(new InputStreamReader(fis));
            String text = br.readLine();
            int newStartIndex = Integer.parseInt(text);
    
            //新开始位置减去原本的开始位置,得到已经下载的数据长度
            int alreadyDownload = newStartIndex - startIndex;
            //把已经下载的长度设置入进度条
            progress += alreadyDownload;

    添加文本框显示百分比进度

            tv.setText(progress * 100 / pb.getMax() + "%");

    HttpUtils的使用

    HttpUtils本身就支持多线程断点续传,使用起来非常的方便

    • 创建HttpUtils对象
            HttpUtils http = new HttpUtils();
    • 下载文件
            http.download(url, //下载请求的网址
                    target, //下载的数据保存路径和文件名
                    true, //是否开启断点续传
                    true, //如果服务器响应头中包含了文件名,那么下载完毕后自动重命名
                    new RequestCallBack<File>() {//侦听下载状态
    
                //下载成功此方法调用
                @Override
                public void onSuccess(ResponseInfo<File> arg0) {
                    tv.setText("下载成功" + arg0.result.getPath());
                }
    
                //下载失败此方法调用,比如文件已经下载、没有网络权限、文件访问不到,方法传入一个字符串参数告知失败原因
                @Override
                public void onFailure(HttpException arg0, String arg1) {
                    tv.setText("下载失败" + arg1);
                }
    
                //在下载过程中不断的调用,用于刷新进度条
                @Override
                public void onLoading(long total, long current, boolean isUploading) {
                    super.onLoading(total, current, isUploading);
                    //设置进度条总长度
                    pb.setMax((int) total);
                    //设置进度条当前进度
                    pb.setProgress((int) current);
                    tv_progress.setText(current * 100 / total + "%");
                }
            });

    示例

        package com.hyman.mobilemultidownload;
        import java.io.BufferedReader;
        import java.io.File;
        import java.io.FileInputStream;
        import java.io.InputStream;
        import java.io.InputStreamReader;
        import java.io.RandomAccessFile;
        import java.net.HttpURLConnection;
        import java.net.URL;
        import android.os.Bundle;
        import android.os.Environment;
        import android.os.Handler;
        import android.app.Activity;
        import android.view.Menu;
        import android.view.View;
        import android.widget.ProgressBar;
        import android.widget.TextView;
    
        public class MainActivity extends Activity {
    
        static int ThreadCount = 3;
        static int finishedThread = 0;
    
        int currentProgress;
        String fileName = "QQPlayer.exe";
        //确定下载地址
        String path = "http://192.168.13.13:8080/" + fileName;
        private ProgressBar pb;
        TextView tv;
    
        Handler handler = new Handler(){
            public void handleMessage(android.os.Message msg) {
                //把变量改成long,在long下运算
                tv.setText((long)pb.getProgress() * 100 / pb.getMax() + "%");
            }
        };
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            pb = (ProgressBar) findViewById(R.id.pb);
            tv = (TextView) findViewById(R.id.tv);
        }
    
        public void click(View v){
    
            Thread t = new Thread(){
                @Override
                public void run() {
                    //发送get请求,请求这个地址的资源
                    try {
                        URL url = new URL(path);
                        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                        conn.setRequestMethod("GET");
                        conn.setConnectTimeout(5000);
                        conn.setReadTimeout(5000);
    
                        if(conn.getResponseCode() == 200){
                            //拿到所请求资源文件的长度
                            int length = conn.getContentLength();
    
                            //设置进度条的最大值就是原文件的总长度
                            pb.setMax(length);
    
                            File file = new File(Environment.getExternalStorageDirectory(), fileName);
                            //生成临时文件
                            RandomAccessFile raf = new RandomAccessFile(file, "rwd");
                            //设置临时文件的大小
                            raf.setLength(length);
                            raf.close();
                            //计算出每个线程应该下载多少字节
                            int size = length / ThreadCount;
    
                            for (int i = 0; i < ThreadCount; i++) {
                                //计算线程下载的开始位置和结束位置
                                int startIndex = i * size;
                                int endIndex = (i + 1) * size - 1;
                                //如果是最后一个线程,那么结束位置写死
                                if(i == ThreadCount - 1){
                                    endIndex = length - 1;
                                }
                                //System.out.println("线程" + i + "的下载区间是:" + startIndex + "---" + endIndex);
                                new DownLoadThread(startIndex, endIndex, i).start();
                            }
                        }
                    } catch (Exception e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            };
            t.start();
        }
    
        class DownLoadThread extends Thread{
            int startIndex;
            int endIndex;
            int threadId;
    
            public DownLoadThread(int startIndex, int endIndex, int threadId) {
                super();
                this.startIndex = startIndex;
                this.endIndex = endIndex;
                this.threadId = threadId;
            }
    
            @Override
            public void run() {
                //再次发送http请求,下载原文件
                try {
                    File progressFile = new File(Environment.getExternalStorageDirectory(), threadId + ".txt");
                    //判断进度临时文件是否存在
                    if(progressFile.exists()){
                        FileInputStream fis = new FileInputStream(progressFile);
                        BufferedReader br = new BufferedReader(new InputStreamReader(fis));
                        //从进度临时文件中读取出上一次下载的总进度,然后与原本的开始位置相加,得到新的开始位置
                        int lastProgress = Integer.parseInt(br.readLine());
                        startIndex += lastProgress;
    
                        //把上次下载的进度显示至进度条
                        currentProgress += lastProgress;
                        pb.setProgress(currentProgress);
    
                        //发送消息,让主线程刷新文本进度
                        handler.sendEmptyMessage(1);
                        fis.close();
                    }
                    System.out.println("线程" + threadId + "的下载区间是:" + startIndex + "---" + endIndex);
                    HttpURLConnection conn;
                    URL url = new URL(path);
                    conn = (HttpURLConnection) url.openConnection();
                    conn.setRequestMethod("GET");
                    conn.setConnectTimeout(5000);
                    conn.setReadTimeout(5000);
                    //设置本次http请求所请求的数据的区间
                    conn.setRequestProperty("Range", "bytes=" + startIndex + "-" + endIndex);
    
                    //请求部分数据,相应码是206
                    if(conn.getResponseCode() == 206){
                        //流里此时只有1/3原文件的数据
                        InputStream is = conn.getInputStream();
                        byte[] b = new byte[1024];
                        int len = 0;
                        int total = 0;
                        //拿到临时文件的输出流
                        File file = new File(Environment.getExternalStorageDirectory(), fileName);
                        RandomAccessFile raf = new RandomAccessFile(file, "rwd");
                        //把文件的写入位置移动至startIndex
                        raf.seek(startIndex);
                        while((len = is.read(b)) != -1){
                            //每次读取流里数据之后,同步把数据写入临时文件
                            raf.write(b, 0, len);
                            total += len;
                            System.out.println("线程" + threadId + "下载了" + total);
    
                            //每次读取流里数据之后,把本次读取的数据的长度显示至进度条
                            currentProgress += len;
                            pb.setProgress(currentProgress);
                            //发送消息,让主线程刷新文本进度
                            handler.sendEmptyMessage(1);
    
                            //生成一个专门用来记录下载进度的临时文件
                            RandomAccessFile progressRaf = new RandomAccessFile(progressFile, "rwd");
                            //每次读取流里数据之后,同步把当前线程下载的总进度写入进度临时文件中
                            progressRaf.write((total + "").getBytes());
                            progressRaf.close();
                        }
                        System.out.println("线程" + threadId + "下载完毕-------------------");
                        raf.close();
    
                        finishedThread++;
                        synchronized (path) {
                            if(finishedThread == ThreadCount){
                                for (int i = 0; i < ThreadCount; i++) {
                                    File f = new File(Environment.getExternalStorageDirectory(), i + ".txt");
                                    f.delete();
                                }
                                finishedThread = 0;
                            }
                        }
    
                    }
                } catch (Exception e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
    }
    
    
    展开全文
  • 通过代码实例的展示,让你轻松学会点多点线程断点续传的实现原理。 另外,代码注释详细、清晰,容易理解。
  • 点对点多线程断点续传软件《圣》源代码.rar
  • Android 多线程断点续传下载工具类实现

    最近项目里用到了断点续传下载,所以在博客里做个记录,便于以后回顾。

    背景知识

    依赖于Http/206响应

    首先你需要知道文件大小以及远程服务器是否支持HTTP 206请求.使用curl命令可以查看任意资源的HTTP头,使用下面的curl命令可以发送一个HEAD请求

    $ curl -I http://avatar.csdn.net/D/0/9/1_wz249863091.jpg
    这张图是我在CSDN博客的头像

    这里写图片描述

    这里只看2个属性

    Accept-Ranges: bytes - 该响应头表明服务器支持Range请求,以及服务器所支持的单位是字节(这也是唯一可用的单位).我们还能知道:服务器支持断点续传,以及支持同时下载文件的多个部分,也就是说下载工具可以利用范围请求加速下载该文件.Accept-Ranges: none 响应头表示服务器不支持范围请求.

    Content-Length: 30220 - Content-Length响应头表明了响应实体的大小,也就是真实的图片文件的大小是30220字节 (30K).

    正常情况下,我们不指定range属性的时候,默认值都是0-length,也就是全量下载,返回的code就是200
    如果我们指定了range,那么返回的code就是206

    代码实现

    根据上面的背景知识,可以写一个工具类,便于整个项目使用

    package com.example.tony.myapplication;
    
    import android.os.Environment;
    import android.text.TextUtils;
    
    import java.io.BufferedReader;
    import java.io.File;
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.FileReader;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.RandomAccessFile;
    import java.net.HttpURLConnection;
    import java.net.MalformedURLException;
    import java.net.URL;
    
    /**
     * 断点续传工具类
     *
     * @author Tony.W
     */
    public class DownloadUtil {
        private static final String RANGE = "Range";
        private static final String BYTE = "bytes=";
        private static final String TO = "-";
        private static final String RWS = "rws";
        private static final String SLASH = "/";
        private static final String UTF_8 = "utf-8";
    
        //Http协议--部分下载的状态码
        private static final int HTTP_PARTIAL_CODE = 206;
        //Htpp协议--下载成功的状态码
        private static final int HTPP_SUCCESS_CODE = 200;
        //Http超时时间
        private static final int TIME_OUT = 3000;
        //默认下载线程数
        private static final int DEFALUT_THREAD_COUNT = 3;
    
        /**
         * 断点续传下载
         *
         * @param path 资源路径
         * @param threadCount 下载线程数
         * */
        public static void down(String path, int threadCount){
            URL url = null;
            HttpURLConnection conn = null;
            RandomAccessFile raf = null;
            try {
                url = new URL(path);
                conn = (HttpURLConnection) url.openConnection();
                if (conn.getResponseCode() == HTPP_SUCCESS_CODE){
                    //文件长度
                    int fileLen = conn.getContentLength();
                    //缓存文件名字
                    String cacheFileName = path.substring(path.lastIndexOf(SLASH) + 1);
                    File file = new File(Environment.getExternalStorageDirectory(), cacheFileName);
                    raf = new RandomAccessFile(file, RWS);
                    raf.setLength(fileLen);
                    int partialLen = fileLen / threadCount;
                    for(int i = 0;i<threadCount;i++){
                        new DownloadThread(url, i, partialLen, file);
                    }
                }
    
            } catch (MalformedURLException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }finally {
                if(raf != null){
                    try {
                        raf.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    
        /**
         * 断点续传下载,默认开启3个线程下载
         *
         * @param path 资源路径
         * */
        public static void down(String path){
            down(path, DEFALUT_THREAD_COUNT);
        }
    
    
        private static final class DownloadThread extends  Thread{
            //下载资源的url
            private URL url;
            //下载的线程Id
            private int threadId;
            //每个线程需要下载的长度
            private int particalLen;
            //缓存文件地址
            private File file;
            //是否暂停下载
            private boolean isPause = false;
    
            /**
             * 下载线程
             * */
            public DownloadThread(URL url, int threadId, int particalLen, File file){
                this.file = file;
                this.url = url;
                this.particalLen = particalLen;
                this.threadId = threadId;
            }
    
            /**
             * 从文件读取数据
             * */
            public static String readFromFile(File file) throws IOException {
                if (!file.exists() || file.isDirectory()) {
                    return null;
                }
                BufferedReader br = new BufferedReader(new FileReader(file));
                String temp = null;
                StringBuffer sb = new StringBuffer();
                temp = br.readLine();
                while (temp != null) {
                    sb.append(temp);
                    temp = br.readLine();
                }
                return sb.toString();
            }
    
            /**
             * 向文件写入数据
             * */
            public static void writeToFile(File file, String data) throws IOException {
                if (!file.exists()) {
                    file.createNewFile();
                }
                //重新写入,不是追加写入模式
                FileOutputStream out = new FileOutputStream(file, false);
                out.write(data.getBytes(UTF_8));
                out.close();
            }
    
            @Override
            public void run() {
                downTask();
            }
    
            private void downTask(){
                //缓存文件名字
                String cacheFileName = Environment.getExternalStorageDirectory() + url.getPath().substring(url.getPath().lastIndexOf(SLASH) + 1 + threadId);
                File cacheFile = new File(cacheFileName);
                //读取缓存文件,判断是否之前已经有下载部分
                float hasDownLen = 0;
                //下载开始位置
                int start = 0 + threadId * particalLen;
                //下载结束为止
                int end = (0 + threadId + 1) * particalLen -1;
    
                String data = null;
                try {
                    data = readFromFile(cacheFile);
                } catch (IOException e) {
                    e.printStackTrace();
                }
    
                if (!TextUtils.isEmpty(data)) {
                    hasDownLen = Float.valueOf(data);
                    start += hasDownLen;
                }
    
                HttpURLConnection conn = null;
                try {
                    conn = (HttpURLConnection) url.openConnection();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                if(conn == null){
                    return;
                }
    
                conn.setReadTimeout(TIME_OUT);
                //指定下载部分,如果超过服务器部分,以服务器为准
                conn.setRequestProperty(RANGE, BYTE + start + TO + end);
                RandomAccessFile raf = null;
                InputStream in = null;
                try {
                    if (conn.getResponseCode() == HTTP_PARTIAL_CODE) {
                        raf = new RandomAccessFile(file, RWS);
                        raf.seek(start);
                        in = conn.getInputStream();
                        //分段下载,每段大小为1024个字节
                        byte[] buf = new byte[1024];
                        int len = 0;
                        //如果读到最后,就跳出循环
                        while ((len = in.read(buf)) != -1) {
                            //将下载的数据存到文件
                            raf.write(buf);
                            //记录下载长度
                            hasDownLen += len;
                            writeToFile(cacheFile, String.valueOf(hasDownLen));
                        }
                        //如果完成该部分下载,删除缓存文件
                        cacheFile.delete();
                    }
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    try {
                        if (raf != null) {
                            raf.close();
                        }
                        if (in != null) {
                            in.close();
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    

    这里只实现了多线程断点续传功能,如果要结合UI,可以加入一个Handler。
    但是需要主要,由于都是static的静态方法,如果处理Handler的时候需要格外小心,不要造成内存泄漏

    另外可以观察DDMS,在下载的时候,会产生N+1个文件,N就是你开启线程的数量,当下载全部完成后,只会留下一个数据文件,其余临时缓存文件都会被删除

    附件已经上传,有需要的朋友可以下载
    http://download.csdn.net/detail/wz249863091/9566461
    如果有什么问题,请留言或者私信,第一时间会做出合理修改

    展开全文
  • 这是java实现多线程断点续传下载功能的源代码,对于学习java网络编程、多线程等基础知识的同学有一定的参考作用,希望能够这个资源能够对你们有帮助
  • ftp协议实现多线程断点续传代码 c++ builder.rar
  • Android多线程断点续传

    千次阅读 2016-07-13 20:34:48
    那么,多线程断点需要什么功能?1.多线程下载,2.支持断点。使用多线程的好处:使用多线程下载会提升文件下载的速度。那么多线程下载文件的过程是: (1)首先获得下载文件的长度,然后设置本地文件的长度。 ...
  • 那么,多线程断点需要什么功能? 1.多线程下载, 2.支持断点。 使用多线程的好处:使用多线程下载会提升文件下载的速度。那么多线程下载文件的过程是: (1)首先获得下载文件的长度,然后设置本地文件的长度。 ...
  • Android多线程断点续传下载器,很好的学习资料,包括完整代码以及详细注释。 无须其他配置,服务器端只需建立一个Dynamic project,然后包含一个mp3文件即可。
  • 最近学习了Android多线程断点续传,这里做个小小的总结,我觉得下载的几处关键:创建临时文件并计算每个线程需要下载的部分size,每个线程需要下载的开始和结束startIndex和endIndexe,使用获取的流的长度除以size就...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 12,925
精华内容 5,170
关键字:

多线程断点续传代码