上篇博客是对从 Teahour.FM 下载 podcast 的简单记录,其中提到了那个下载程序支持某些「高级」特性:多线程、断点续传、超时重传。其实,这些特性大家也经常会听到,因为很多专业的下载工具都支持。

如何实现这些特性呢?技术上并不复杂。接下来,我就抛砖引玉,写一下自己的认识。如果有写得不对或不准确的地方,请指正。

 

需要注意的是:

  • 下面的讨论仅适用于 HTTP 协议的文件下载,其它协议不在讨论之列。
  • 下面的讨论重原理轻实现,你可以选择用自己熟悉的编程语言去实现这些特性。我用的是 Python 3(主要用 urllibthreading 两个模块),实现起来比较简单。完整的代码在此

 

下面,分别讨论这几个特性:

  • 多线程

    首先需要说明的是,这里的「多线程」是指用多个线程下载同一个文件,而不是说有多个线程、然后每个线程分别负责下载一个文件。

    多线程能够显著提高文件下载速度。如何实现多线程下载文件呢?原理很简单,就是每个线程只负责下载文件的某一部分,待文件的所有部分都下载完成后,将文件的各个部分拼接起来,就是最终要下载的目标文件了。其中会利用到 HTTP response header 的 Content-Length 字段和 HTTP request header 的 Range 字段。

    完整过程如下:

    1. 对目标 URL 发出 HTTP 请求。根据 HTTP response header 的 Content-Length 字段,得到要下载的目标文件大小 N。
    2. 根据线程数(自己视实际情况而定)和文件大小,划分各个线程的下载任务,各个线程的下载任务量最好接近。比如,如果有 3 个线程,则 3 个线程的任务分配大致分别为:[0, (1/3)*N], [(1/3)*N, (2/3)*N], [(2/3)*N, N]。
    3. 分配线程,各线程根据已分配的下载任务量。根据 HTTP request header 的 Range 字段,可以指定请求实体的子范围,也就是只下载文件的一部分。
    4. 各线程都下载完成后,将文件的各部分拼接起来,得到要下载的完整的目标文件,再将已下载的文件各部分删除。

    关于 HTTP request header 的 Range 字段,可以参考这个链接

  • 断点续传

    断点续传的原理比较简单:先得到已下载部分的大小,利用 HTTP request header 的 Range 字段,接着继续下载未下载的部分即可。

    文件的断点续传与多线程本身没有关系,无论单/多线程下载,都可以使用断点续传技术。但是如果是多线程环境下的断点续传,需要保证前后两次下载的线程任务分配方案完全一致。

  • 超时重传

    超时重传也容易理解:对于发出的 HTTP 请求,设置一个 timeout 超时参数。如果发出的 HTTP 请求超时(在 timeout 时间内仍然没有建立连接),则重新发出 HTTP 请求。

 

最后,如何验证采用了这些「高级」特性下载下来的文件是正确的呢?

其实就是与以普通方式下载下来的文件做比较,比较两种不同方式下载下来的文件是否相同了。

Linux 下可以使用的工具有:diffcmp 以及一些 checksum 工具(md5sum, sha1sum 等)。具体可以参考 StackOverflow 的这个链接

另外,Python 自身也提供了一个用于文件比较的模块 filecmp,可以用来比较两个文件是否相同。

 

参考链接:

Leave a Reply

Your email address will not be published. Required fields are marked *