Android多线程断点续传

来源:互联网 发布:阿里云服务器上传网站 编辑:程序博客网 时间:2024/05/19 23:55

断点续传

思考:

1.断点续传,就是当一次下载任务由于某些因素中断下载,能够继续上次下载的位置继续下载,不必在从头开始。2.支持多线程,就是把一个下载任务分配给多个线程去下载,每一个线程只下载一部分内容。

分析:

http协议,一次下载请求,就是请求远端服务器一个资源(url),建立TCP链接,把服务器资源写入本地的一个过程。

那么如果要实现断点续传的必要条件:

1.我们能够知道下载资源的大小(多少字节)2.我们可以请求某一部分资源(一个文件的某一段字节数据)3.服务器告诉我们返回的资源总大小和偏移量(从某一位置开始返回数据)

查找http协议规范

http/1.1请求头中有下面相关字段请求头GET /file.zip HTTP/1.1 Range:bytes=680- // 从某一个位置开始请求...响应头HTTP/1.1 206 Partial ContentAccept-Ranges : bytes ///告诉客户端,我们是支持断点传输的Content-Length : //返回文件总长度(少字节)Content-Range :bytes //返回的字节范围...

模拟一次单线程断点续传:

1.客户端发起请求,下载file.zipGET /file.zip HTTP/1.1 2.服务器收到请求返回HTTP/1.1 200 OKAccept-Ranges : bytes ///告诉客户端,我们是支持断点传输的Content-Length : 2000 //返回文件总长度(2000字节)...3.客户端在下载到600字节的时候突然间中断了下载链接,然后再次发起续传请求GET /file.zip HTTP/1.1 Range:bytes=600- // 告诉服务器我从600字节开始下载数据...4.服务器收到请求给予回应HTTP/1.1 206 Partial Content // 注意响应码206,不是200Accept-Ranges : bytes ///告诉客户端,我们是支持断点传输的Content-Length : 1400//返回剩余未下载文件总长度Content-Range :bytes 600-1399//返回的字节范围...多线程的下载其实就是,在第一次请求只拿到要下载文件的总大小,然后计算分配给几个线程,每个线程具体下载哪一段数据,然后在去请求对应的数据。

代码设计与实现:

相关技术介绍:

1.HttpURLConnection

URL url = new URL(mFileInfo.getUrl());//请求的链接HttpURLConnection conn = (HttpURLConnection) url.openConnection();//打开链接conn.setConnectTimeout(5 * 1000);//设置超时时间conn.setRequestMethod("GET");//设置请求方法conn.setRequestProperty("Range", "bytes=" + start + "-" +end);// 设置下载文件开始到结束的位置int code = conn.getResponseCode();//拿到响应码conn.getInputStream();// 拿到输入流

2.RandomAccessFile

RandomAccessFile是不属于InputStream和OutputStream类系的。实际上,除了实现DataInput和DataOutput接口之外(DataInputStream和DataOutputStream也实现了这两个接口),它和这两个类系毫不相干,甚至都没有用InputStream和OutputStream已经准备好的功能;它是一个完全独立的类,所有方法(绝大多数都只属于它自己)都是从零开始写的。这可能是因为RandomAccessFile能在文件里面前后移动,所以它的行为与其它的I/O类有些根本性的不同。总而言之,它是一个直接继承Object的,独立的类。

http://www.android-doc.com/reference/java/io/RandomAccessFile.html

void    seek(long offset)//移动到某一位置,多线程下载(每个线程写入位置都不同)

代码设计思路(多线程下载支持断点续传)

1.使用数据库来存储每一个线程信息的下载信息,(线程id,下载的url,起始下载位置,终止下载位置,当前下载进度)2.第一次请求的时候,计算好每一个线程需要下载的数据块,存入数据库,并在每一次写入数据时,修改进度3.还原下载的时候,从数据库拿到这些信息,接着未完成的请求。4.如果当前线程完成自己的任务,就从数据库里面移除

核心代码

获取文件大小

HttpURLConnection conn = null;            RandomAccessFile raf = null;            try {                URL url = new URL(mFileInfo.getUrl());                conn = (HttpURLConnection) url.openConnection();                conn.setConnectTimeout(5 * 1000);                conn.setRequestMethod("GET");                int code = conn.getResponseCode();                int length = -1;                if (code == HttpURLConnection.HTTP_OK) {                    length = conn.getContentLength();                }                //如果文件长度为小于0,表示获取文件失败,直接返回                if (length <= 0) {                    return;                }                // 判断文件路径是否存在,不存在创建                File dir = new File(DownloadPath);                if (!dir.exists()) {                    dir.mkdir();                }                // 创建本地文件                File file = new File(dir, mFileInfo.getFileName());                raf = new RandomAccessFile(file, "rwd");                raf.setLength(length);                // 设置文件长度                mFileInfo.setLength(length);                // 将FileInfo对象传给Handler                Message msg = Message.obtain();                msg.obj = mFileInfo;                msg.what = MSG_INIT;                mHandler.sendMessage(msg);            } catch (Exception e) {                e.printStackTrace();            } finally {                if (conn != null) {                    conn.disconnect();                }                try {                    if (raf != null) {                        raf.close();                    }                } catch (IOException e) {                    e.printStackTrace();                }            }

下载线程任务

HttpURLConnection conn = null;            RandomAccessFile raf = null;            InputStream is = null;            try {                URL url = new URL(mFileInfo.getUrl());                conn = (HttpURLConnection) url.openConnection();                conn.setConnectTimeout(5 * 1000);                conn.setRequestMethod("GET");                long start = threadInfo.getStart() + threadInfo.getFinished();                // 设置下载文件开始到结束的位置                conn.setRequestProperty("Range", "bytes=" + start + "-" + threadInfo.getEnd());                File file = new File(DownloadService.DownloadPath, mFileInfo.getFileName());                raf = new RandomAccessFile(file, "rwd");                // 设置文件偏移量                raf.seek(start);                mFinished += threadInfo.getFinished();                Log.d("xzq","下载起始位置 = "+start+", mFinished = "+mFinished);                Intent intent = new Intent();                intent.setAction(DownloadService.ACTION_UPDATE);                int code = conn.getResponseCode();                if (code == HttpURLConnection.HTTP_PARTIAL) {                    is = conn.getInputStream();                    byte[] bt = new byte[1024];                    int len = -1;                    while ((len = is.read(bt)) != -1) {                        raf.write(bt, 0, len);                        // 累计整个文件完成进度                        mFinished += len;                        // 累加每个线程完成的进度                        threadInfo.setFinished(threadInfo.getFinished() + len);                        if (mIsPause) {                            mDao.updateThread(threadInfo.getUrl(), threadInfo.getId(), threadInfo.getFinished());                            return;                        }                    }                }                // 标识线程是否执行完毕                isFinished = true;                // 判断是否所有线程都执行完毕                checkAllFinished();            } catch (Exception e) {                e.printStackTrace();            } finally {                if (conn != null) {                    conn.disconnect();                }                try {                    if (is != null) {                        is.close();                    }                    if (raf != null) {                        raf.close();                    }                } catch (IOException e) {                    e.printStackTrace();                }            }

DEMO下载

完整DEMO下载

原创粉丝点击