原理:利用HttpURLConnection进行从网站上下载文件时,我们可以利用HttpURLConnection的setRequestProperty()方法,对我们要读取的字节部分进行控制,比如:
1.Range=0-100代表只读取前100个字节。 2.Range=100-500代表读取从第100个字节开始,读到第500个字节为止。 3.Range=100-则代表从第100个字节开始读取,一直读取到文件末尾结束。
这样我们就可以轻松的实现对文件的各部分的读取了,我们只需要在暂停时记录一下已经读取到的位置,在重新开始的时候利用setRequestProperty()方法设置一下我们要读取的字节位置即可。
这里还有一个问题就是我们应该怎样将读取到的字节流写入文件中,由于File是不支持在指定处写入字节的,因此我们这里要使用RandomAccessFile来进行字节流的写入,RandomAccessFile有一个方法seek(long),允许我们指定要写入的位置。
这样两者结合,就能够轻易的实现文件的断点续传了。
多线程断点续传实现原理相同,只是利用多个线程同时下载,每个线程指定要下载的字节部分,写入到文件的指定部分。故这里只给出了单线程断点续传下载的实现部分:
class SingleDownloadTask{ private String filePath; private int contentLength; private int readLength; private RandomAccessFile file=null; private boolean isPause=false; private URL url; public boolean isDownloading() { return isDownloading; } private boolean isDownloading=false; private Handler mHandler=new Handler(); public SingleDownloadTask(String urlStr,String filePath) { this.filePath=filePath; readLength=0; contentLength=0; try { url=new URL(urlStr); } catch (MalformedURLException e) { e.printStackTrace(); } } //download from pos public void download(){ new Thread(new Runnable() { @Override public void run() { isDownloading=true; HttpURLConnection conn=null; InputStream is=null; BufferedInputStream bis=null; try { file=new RandomAccessFile(filePath,"rw"); file.seek(readLength); conn= (HttpURLConnection) url.openConnection(); if(readLength==0){ contentLength=conn.getContentLength(); }else{ conn.setRequestProperty("Range","bytes="+readLength+"-"); } is=conn.getInputStream(); bis=new BufferedInputStream(is); byte[] bytes=new byte[1024]; int len=0; while (!isPause&&((len=bis.read(bytes,0,1024)))!=-1){ file.write(bytes,0,len); readLength+=len; mHandler.post(new Runnable() { @Override public void run() { float rate=((float)readLength)/contentLength; mProgressBar.setProgress((int) (100*rate)); mTextView.setText((int)(100*rate)+"%"); } }); } isDownloading=false; if (readLength>=contentLength){ mHandler.post(new Runnable() { @Override public void run() { Toast.makeText(MainActivity.this, "文件下载成功,保存在"+filePath , Toast.LENGTH_SHORT).show(); mImageView.setImageBitmap(BitmapFactory.decodeFile(filePath)); } }); } file.close(); } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }finally { if (bis!=null){ try { bis.close(); if (is!=null){ is.close(); } if (conn!=null){ conn.disconnect(); } } catch (IOException e) { e.printStackTrace(); } } } } }).start(); } public void start(){ isPause=false; download(); } public void pause(){ isPause=true; } }
Demo: