python大文件分段下载器

介绍了一种使用Python实现的多线程大文件分段下载技术,该技术适用于超清视频、大文本等超大文件的高效下载。通过将文件分割成小块并利用多线程同时下载各个部分,最后合并成完整文件,显著提高了下载速度。

python大文件分段下载器

本次使用到的技术点:大文件分割、多线程下载同一个文件、队列管理待下载文件片段、os.path模块管理本地文件、requests请求下载视频

一、项目由来

网上很少关于python使用多线程分段下载超清视频、大文本等超大文件的资料,由于多线程适合io密集型和网络请求,所以使用多线程下载大文件能极大的提高下载效率。本次需求产生的原因是朋友在做视频爬取项目,已经提取到了视频下载地址的情况下产生的,由于需要下载大量的视频,使用单线程下载速度极慢,又没有使用scrapy框架,所以本人就想着开辟多线程下载视频,于是写好了一个多线程下载视频的文件,但是由于下载的都是超清视频,下载速度还是不够快,本人就想到了将文件分段下载再合成,使用多线程下载一个文件不就又比单线程下载一个文件快了吗,估计迅雷也是这样实现的吧。

二、下载思路

  1. 获取要下载的视频的大小
  2. 将视频大小分割成N段每次每段的大小为1M(1024*1024)
  3. 将每段加入队列,开启多线程请求获取每段文件保存到本地
  4. 所有的文件片段下载完成将文件片段合成

三、具体实现

本次需要构建两个类

  1. 文件管理类FileManger

文件管理类FileManger类,负责文件的管理(分割文件、保存文件、合成文件片段、创建文件目录、获取本地保存文件片段编号列表等)
文件管理类使用多线程保存文件、使用os.path模块管理文件

  1. 下载器类Downloader

    下载器类主要用于发送网络请求,分段获取视频文件,通过文件管理类将文件片段保存到本地并合成。

四、技术点要点

  1. 文件分割
    def split_file(self, start=0, end=None, section_size=1024 * 1024):
        """
        分割文件
        :param start:开始位置,默认从头开始
        :param end: 结束位置,默认到文件末尾
        :param section_size: 分割片段的大小,默认为1M
        :return: 字典格式,段数count及片段列表sections
        """

        if end:
            # 用户定义了分割大小
            end = end
        elif os.path.exists(self.file_path):
            # 用户未定义分割大小,系统存在文件
            end = os.path.getsize(self.file_path)
        else:
            # 其他情况
            return False

        if end < start:
            # 末尾大于开始
            return False

        # 获取分割段数
        n = (end - start) // section_size + 1
        # 分割文件,将分割结果加入下载队列中
        sections = {str(i): (i * section_size, (i + 1) * section_size - 1 if i + 1 != n else end - start) for i in
                    range(n)}
        return sections

  1. 文件合成
    def merge_section(self, path=None, count=None):
        """
        合并本地的文件片段
        :param path: 本地片段所在目录
        :return: boolean
        """
        # 获取当前管理的文件的文件夹名
        path = path if path else os.path.splitext(self.file_path)[0]
        index = 0
        # print(path)
        if not os.path.isdir(path):
            # 文件夹不存在
            return False

        # 创建一个文件字典
        file = dict()
        file_size = 0
        file_name = ""
        for root, dirs, names in os.walk(path):
            for name in names:
                # 获取后缀名
                id, ext = os.path.splitext(name)
                # 获取所有具有编号的mp4文件
                if ext == '.mp4' and id.isdigit():
                    # print(name)
                    # mp4文件原始地址
                    section_path = os.path.join(root, name)
                    # print(type(section_path), section_path)
                    file[id] = section_path
                    file_size += os.path.getsize(section_path)
                    file_name = os.path.join(path, f"{os.path.split(path)[-1]}{ext}")

        # 没有获取到文件
        if len(file.keys()) <= 0:
            return False

        # 如果用户输入count使用用户的count数
        count = count if count else len(file.keys())
        if count > len(file.keys()):
            # 若用户输入的count大于实际的文件数,使用实际的文件数
            count = len(file.keys())
            file_size = 0
            for i in range(count):
                # 重新获取文件的大小
                file_size += os.path.getsize(file.get(str(i)))

        if os.path.exists(file_name):
            # 文件已经合成完毕
            if os.path.getsize(file_name) >= file_size:
                # print("文件已经合成完毕,不需要再次合成")
                return False

        while index < count:
            # 打开本地文件获取文件片段,合并文件片段
            path = file.get(str(index))
            if path == None:
                print(f"未找到文件编号:{index}")
                break
            # 获取数据将数据写入本地
            f = open(path, mode="rb")
            data = f.read()
            file_path = os.path.join(os.path.splitext(self.file_path)[0], os.path.basename(self.file_path))
            fp = open(file_path, mode="ab")
            fp.write(data)
            fp.close()
            f.close()
            index += 1

  1. 文件多线程保存
    def save_section(self, section_dict):
        "
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值