在写下载模块,遇到一个问题:下载的时候如何又快又准确地记录下载的进度?
缘由:记录下载进度是为了下次继续下载时做文件校验,比如有没有被篡改。
目前的方案:(伪代码)
while(true)
{
byte[] buffer = connection.read();
file.write(buffer);
database.save(file.length());
}
出现一个问题:
如果不加 database.save ,下载速度可以达到正常的带宽速度。
如果加了,下载速度就会慢,并且很明显。
但是为了做校验,肯定需要 [实时] 保存下载的文件大小。
我想用一个 tempValue [实时] 记录一下进度,最后再刷到数据库中。
但是有个问题:如果程序异常终止,就会导致 tempValue 没有被刷到数据库中。
这样的话,有什么比较好的解决方案??
1
bigboyq 2016-10-13 10:17:00 +08:00 via iPhone
亲,你这个肯定会慢,因为 database save 方法被频繁调用,而且是 block 的,如果你把 database save 异步就不会影响,或者定量写入,比如 1MB 写一次,也会降低影响。
异常的问题可以通过异常处理模块补充。 |
2
814084764 OP @bigboyq 异常的地方肯定有很多,除了下载模块的异常,还有其他模块的异常,如何去做补充?
做一个全局的接收异常的方法?有些未捕获到的异常,又怎么处理呢? 所以感觉异常这块的处理还是有点麻烦呢。。。束手无策的赶脚。 要不就是改成写入到轻量级的文件中? |
3
Karblue 2016-10-13 13:31:06 +08:00
不要写完就刷新硬盘。 IO 操作是很费时间的。存到缓存。然后再把缓存定时写出。
例如:把文件数据暂时存在全局变量中。开单独的线程去写出全局变量的文件 |
4
lrh3321 2016-10-13 13:44:03 +08:00
不支持 Linux ,我还是继续用 命令行吧
|
5
lrh3321 2016-10-13 13:45:46 +08:00
回错了,要回另外个帖子的。
=============== 我觉得做个几 MB 的缓存,满了再写入磁盘。丢数据也就是丢缓存里这几 MB |
6
limhiaoing 2016-10-13 13:46:30 +08:00 via iPhone
个人观点这个方法不可行。
可以这么做,数据库记录已下载大小和 hash ,先写下载的数据到磁盘后记录下载进度和 hash 。 |
7
limhiaoing 2016-10-13 13:49:58 +08:00 via iPhone
这样既是异常终止在写完下载数据和记录下载进度之间也不要紧,根据上次记录的下载进度丢弃多余的数据,校验记录的 hash 即可。
|
8
limhiaoing 2016-10-13 13:51:35 +08:00 via iPhone
我估计迅雷的断点续传是用类似的方法,不过因为它是分块下载,所以 hash 应该也是分块计算的。
|
9
ryd994 2016-10-13 14:46:24 +08:00
其实简单办法就是检查当前文件大小
直接从那个长度开始 这也是 wget 的做法 至于校验数据完整性……除非重新下一遍,否则 http 协议本身不含 checksum 做不到 |
10
bigboyq 2016-10-13 18:36:40 +08:00 via iPhone
再起一个线程做 database.save 就好了,但是你又回来这里问数据库 update 慢怎么办。。。。。。
|
11
icedx 2016-10-13 18:56:18 +08:00 via Android
别说我不友善啊
你是不是 SS database.save(file.length()); 你这样能不慢么 |
12
dlllcs 2016-10-13 22:35:50 +08:00
我觉得可以新建一个 Buffer ,大小差不多 32M 这样,每下载 32M 或者达到了 5 秒钟,就刷新进文件,这样比较好吧,这样 IO 比较少,而且丢数据也就丢最近几秒钟的数据
|
13
limhiaoing 2016-10-13 23:00:08 +08:00
单单靠文件长度来判断文件是否被篡改是不可靠的,文本被篡改不一定会导致文件长度改变。
即使你这两句之间没有可能抛出异常,但是断电呢? file.write(buffer); database.save(file.length()); 所以,记录文件长度只能用于丢弃多余的数据,而不能用来校验文件是否被篡改。 用 hash+长度来记录已下载的文件就不需要实时的去保存长度了。可以每下载 10MB 保存一次,这样如果进程异常结束,最多就是丢弃 10MB 的数据。 |