多线程断点下载的一个基础了解
(初级-本地下载)
1.RandomAccessFile
1)简单介绍
是一种任意存取流、是处理流的一种,其对象包含一个记录指针能以任意访问的方式,程序可以跳到文件的任意位置进行读写。因而可以利用这个流,利用多线程来实现多断点下载,加快下载速度。
继承于java.lang.Object类,位于java.io包下。
2)构造器
创建实例时需要指定一个mode参数,用来指定访问模式。
本文主要用只读和读写两种模式
3)两个主要方法
-1: long getFilePointer() 获取文件记录指针当前的位置
-2 : void seek(long pos) 将文件指针定位到指定pos位置
位置的单位都是B,即字节
-3 :使用实例:
用任意存取流的对象来调用
randomAccessFile.seek(100); // 将文件指针移动到第100个字节的位置。
2.文件记录指针
这对于本文实现多线程的断点下载很重要,条件基于这个指针指向而设置。
1)简单介绍
"文件记录指针" 通常指的是一个指向文件中某个位置的指针。这个指针可以用于定位文件中的数据,以便读取或写入特定的位置。文件记录指针的概念常常与随机访问文件相关。
2)文件指针的定位
文件指针指示了文件中当前读取或写入的位置。在顺序访问中,文件指针从文件的开头逐渐移动到文件的末尾。而在随机访问中,可以通过文件指针直接跳转到文件的任意位置。
不指定的情况下不会指向已经读写的位置(除了顺序写时,最终会指向文件末尾已经写完的最后一个字节)
3)初始位置
文件记录指针的起始位置为0是一种通用的约定,而不是一个绝对的规定。这约定在计算机科学中是一种普遍的惯例,多数编程语言和文件系统都遵循这个规定。
这种约定的历史起源可以追溯到低级别的硬件和操作系统设计。在很多系统中,文件被认为是一系列的字节或块,这些字节按照从0开始的索引进行编号。因此,将文件记录指针的初始位置设置为0是直观且方便的选择。
当你打开一个文件时,文件记录指针通常会被设置为文件的起始位置,即0。这使得文件指针的移动更容易理解,因为它与文件中的字节索引直接对应。
4)顺序读取的终止位置
指向文件的末尾。
例如图片有888888B则指针最终会指向位置888888B,是同样的。
3.run方法
1)成员变量设置
开始下载的位置和终止下载的位置
两个File对象,方便读写
注意,由于是继承于Tread类的方法,这些变量不是多线程共享的,每个线程都有自己的一套。
2)处理
最后一次要达到规定的线程下载结束位置时,通过处理用于传输的数组大小来使得正好传输到满足要求的数据:
if ((this.trunPosition < this.start + num + 1024 - 1)){
b = new byte[(int) (this.trunPosition - num - this.start + 1)];
}
这使得该线程下载文件终止的位置能恰好在规定处,使得多个线程的下载能合在一起。而该线程的任意存取流对象的记录指针——>指向规定下载结束位置+1或文件末尾位置
if(raFin.getFilePointer() >= this.trunPosition){
//System.out.println(Thread.currentThread().getName()+" "+len);
//System.out.println(Thread.currentThread().getName()+" "+b.length);
//System.out.println(raFout.getFilePointer());
break;
}
//采用继承于Thread类的重写run方法创建线程
class DownMuldo extends Thread {
long start;
long trunPosition;
File filein = new File("E:\\BaiduNetdiskDownload\\wen\\query.avi");
File fileout = new File("E:\\BaiduNetdiskDownload\\wen\\querydownload.avi");
@Override
public void run() {
long allLength = filein.length();
RandomAccessFile raFin = null;
RandomAccessFile raFout = null;
byte[] b = new byte[1024];
int len = 0;
long num = 0;
try {
raFin = new RandomAccessFile(filein, "r");
raFout = new RandomAccessFile(fileout, "rw");
raFin.seek(start);
raFout.seek(start);
while (true) {
if ((this.trunPosition < this.start + num + 1024 - 1)){
b = new byte[(int) (this.trunPosition - num - this.start + 1)];
}
len = raFin.read(b);
raFout.write(b,0,len);
num += (long) len;
System.out.println(Thread.currentThread().getName());
if(raFin.getFilePointer() >= this.trunPosition){
//System.out.println(Thread.currentThread().getName()+" "+len);
//System.out.println(Thread.currentThread().getName()+" "+b.length);
//System.out.println(raFout.getFilePointer());
break;
}
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
try {
if(raFin != null){
raFin.close();
}
if(raFout != null){
raFout.close();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
4.创建线程
初始化专属于线程的一些变量,主要是对起始位置和终止位置的设置,本文采用4部分下载或者说复制。
public class MulRoute {
static boolean flag = true;
public static void main(String[] args) {
DownMuldo m1 = new DownMuldo();
DownMuldo m2 = new DownMuldo();
DownMuldo m3 = new DownMuldo();
DownMuldo m4 = new DownMuldo();
m1.start = 0;
m1.trunPosition = m1.filein.length() / 4 - 1;
m1.setName("下载线程1");
System.out.println(m1.start);
m2.start = m1.trunPosition + 1;
m2.trunPosition = m2.start + m1.filein.length() / 4 - 1 - 1;
m2.setName("下载线程2");
System.out.println(m2.start);
m3.start = m2.trunPosition + 1;
m3.trunPosition = m3.start + m1.filein.length() / 4 - 1 - 1;
m3.setName("下载线程3");
System.out.println(m3.start);
m4.start = m3.trunPosition + 1;
m4.trunPosition = m1.filein.length();
m4.setName("下载线程4");
System.out.println(m4.start);
//m1.start();
//m2.start();
//m3.start();
//m4.start();
}
}
5.总结
1.本文通过仿真,了解多线程断点下载的很初级的方法和理念,能够成功复制/下载视频、文件、图片等。
2.通过使用,熟悉了文件记录指针。
3.后续有待扩展:通过文件记录指针记录终止下载的位置,用户发出重新下载指令后,从记录的位置开始重新下载。只要明白了随机存取的文件记录指针,这实际不难。
4.期待大家的批评指正,感谢。