V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
814084764
V2EX  ›  程序员

[速度 VS 准确] 下载时保存下载进度

  •  
  •   814084764 · 2016-10-13 10:09:05 +08:00 · 3044 次点击
    这是一个创建于 2963 天前的主题,其中的信息可能已经有所发展或是发生改变。

    在写下载模块,遇到一个问题:下载的时候如何又快又准确地记录下载的进度?

    缘由:记录下载进度是为了下次继续下载时做文件校验,比如有没有被篡改。

    目前的方案:(伪代码)

    while(true)

    {

    byte[] buffer = connection.read();

    file.write(buffer);

    database.save(file.length());

    }

    出现一个问题:

    如果不加 database.save ,下载速度可以达到正常的带宽速度。

    如果加了,下载速度就会慢,并且很明显。

    但是为了做校验,肯定需要 [实时] 保存下载的文件大小。

    我想用一个 tempValue [实时] 记录一下进度,最后再刷到数据库中。

    但是有个问题:如果程序异常终止,就会导致 tempValue 没有被刷到数据库中。

    这样的话,有什么比较好的解决方案??

    14 条回复    2016-10-14 10:03:11 +08:00
    bigboyq
        1
    bigboyq  
       2016-10-13 10:17:00 +08:00 via iPhone
    亲,你这个肯定会慢,因为 database save 方法被频繁调用,而且是 block 的,如果你把 database save 异步就不会影响,或者定量写入,比如 1MB 写一次,也会降低影响。
    异常的问题可以通过异常处理模块补充。
    814084764
        2
    814084764  
    OP
       2016-10-13 10:28:00 +08:00
    @bigboyq 异常的地方肯定有很多,除了下载模块的异常,还有其他模块的异常,如何去做补充?
    做一个全局的接收异常的方法?有些未捕获到的异常,又怎么处理呢?
    所以感觉异常这块的处理还是有点麻烦呢。。。束手无策的赶脚。
    要不就是改成写入到轻量级的文件中?
    Karblue
        3
    Karblue  
       2016-10-13 13:31:06 +08:00
    不要写完就刷新硬盘。 IO 操作是很费时间的。存到缓存。然后再把缓存定时写出。
    例如:把文件数据暂时存在全局变量中。开单独的线程去写出全局变量的文件
    lrh3321
        4
    lrh3321  
       2016-10-13 13:44:03 +08:00
    不支持 Linux ,我还是继续用 命令行吧
    lrh3321
        5
    lrh3321  
       2016-10-13 13:45:46 +08:00
    回错了,要回另外个帖子的。
    ===============
    我觉得做个几 MB 的缓存,满了再写入磁盘。丢数据也就是丢缓存里这几 MB
    limhiaoing
        6
    limhiaoing  
       2016-10-13 13:46:30 +08:00 via iPhone
    个人观点这个方法不可行。
    可以这么做,数据库记录已下载大小和 hash ,先写下载的数据到磁盘后记录下载进度和 hash 。
    limhiaoing
        7
    limhiaoing  
       2016-10-13 13:49:58 +08:00 via iPhone
    这样既是异常终止在写完下载数据和记录下载进度之间也不要紧,根据上次记录的下载进度丢弃多余的数据,校验记录的 hash 即可。
    limhiaoing
        8
    limhiaoing  
       2016-10-13 13:51:35 +08:00 via iPhone
    我估计迅雷的断点续传是用类似的方法,不过因为它是分块下载,所以 hash 应该也是分块计算的。
    ryd994
        9
    ryd994  
       2016-10-13 14:46:24 +08:00
    其实简单办法就是检查当前文件大小
    直接从那个长度开始
    这也是 wget 的做法
    至于校验数据完整性……除非重新下一遍,否则 http 协议本身不含 checksum 做不到
    bigboyq
        10
    bigboyq  
       2016-10-13 18:36:40 +08:00 via iPhone
    再起一个线程做 database.save 就好了,但是你又回来这里问数据库 update 慢怎么办。。。。。。
    icedx
        11
    icedx  
       2016-10-13 18:56:18 +08:00 via Android
    别说我不友善啊
    你是不是 SS
    database.save(file.length());
    你这样能不慢么
    dlllcs
        12
    dlllcs  
       2016-10-13 22:35:50 +08:00
    我觉得可以新建一个 Buffer ,大小差不多 32M 这样,每下载 32M 或者达到了 5 秒钟,就刷新进文件,这样比较好吧,这样 IO 比较少,而且丢数据也就丢最近几秒钟的数据
    limhiaoing
        13
    limhiaoing  
       2016-10-13 23:00:08 +08:00
    单单靠文件长度来判断文件是否被篡改是不可靠的,文本被篡改不一定会导致文件长度改变。
    即使你这两句之间没有可能抛出异常,但是断电呢?
    file.write(buffer);
    database.save(file.length());
    所以,记录文件长度只能用于丢弃多余的数据,而不能用来校验文件是否被篡改。
    用 hash+长度来记录已下载的文件就不需要实时的去保存长度了。可以每下载 10MB 保存一次,这样如果进程异常结束,最多就是丢弃 10MB 的数据。
    814084764
        14
    814084764  
    OP
       2016-10-14 10:03:11 +08:00
    @icedx 是有点傻。。哈哈
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2881 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 02:56 · PVG 10:56 · LAX 18:56 · JFK 21:56
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.