V2EX = way to explore
V2EX 是一个关于分享和探索的地方
Sign Up Now
For Existing Member  Sign In
• 请不要在回答技术问题时复制粘贴 AI 生成的内容
814084764
V2EX  ›  程序员

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

  •  
  •   814084764 · Oct 13, 2016 · 3601 views
    This topic created in 3483 days ago, the information mentioned may be changed or developed.

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

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

    目前的方案:(伪代码)

    while(true)

    {

    byte[] buffer = connection.read();

    file.write(buffer);

    database.save(file.length());

    }

    出现一个问题:

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

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

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

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

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

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

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