示例:

#!/usr/bin/env python3
# -*- coding:utf-8 -*-
# @Time: 2020/12/16 10:42
# @Author:zhangmingda
# @File: urllib_multi_download.py
# @Software: PyCharm
# Description: 使用urllib 模块 实现多线程下载某个文件测试 from concurrent.futures import ThreadPoolExecutor, as_completed
from urllib.request import urlopen
from urllib.request import Request
from urllib.request import quote
import json
import math
import os class DownLoader(object):
def __init__(self):
self.part_size = 1024 * 1024 * 10 # 分块下载大小
self.part_thread_num = 10
self.BUFFER_SIZE = 64 * 1024 def download_part(self, encode_url, part_filename, offset, end_bytes):
"""
:param encode_url:经过URL编码的网络地址
:param part_filename: 文件块儿名字
:param offset: 下载字节起始点(包含)
:param end_bytes: 下载字节结束点(包含)
:return: (下载结果)
"""
# 构造请求头
range_header = {
'Range': 'bytes=%s-%s' % (offset, end_bytes)
}
print(range_header)
cur_task_ret = False
expected_file_size = end_bytes - offset + 1
part_req = Request(encode_url,headers=range_header)
with open(part_filename, 'wb') as local_part_fd:
with urlopen(part_req) as req_fd:
while True:
# 一直从网络读数据
data = req_fd.read(self.BUFFER_SIZE)
if not data:
break
local_part_fd.write(data)
if expected_file_size == os.stat(part_filename).st_size:
print('%s 与预期块儿文件大小相符' % part_filename)
cur_task_ret = True
# break
else:
print('%s 与预期块儿文件大小 不符,预期%s字节,实际得到%s 字节' % (
part_filename, expected_file_size, os.stat(part_filename).st_size)) return {part_filename: cur_task_ret} def download(self, url):
finally_filename = os.path.basename(url)
# 将URL编码成%字符串格式
encode_url = quote(url, safe=";/?:@&=+$,")
print(encode_url)
# 构造请求
req = Request(encode_url)
# 发起请求并且获取内容长度
with urlopen(req) as fp:
# print(json.dumps(dir(fp),indent=1))
print(fp.getheaders())
# length = fp.getheader('content-Range')
length = fp.getheader('Content-Length')
length = int(length)
print(type(length))
print('length:', length) # 分块任务列表
thread_list = []
# 每个块儿下载的结果
multi_chunk_download_result = {}
chunk_size = self.part_size
# 计算需要下载的块儿个数
chunk_count = int(math.ceil(length / float(chunk_size)))
pool_args_list = [] # 计算每个块儿请求的字节范围
for i in range(chunk_count):
offset = chunk_size * i
end_bytes = min(chunk_size * (i + 1), length) - 1
# 将一个文件划分的所有块儿任务,添加到任务列表
part_num = i + 1
part_filename = finally_filename + '.' + str(part_num)
# 每个块儿请求的范围,块儿名字,加到线程参数列表
pool_args_list.append((encode_url, part_filename, offset, end_bytes)) # ********开始多线程下载数据,并获取下载结果**************
# 构建线程池实例
tp = ThreadPoolExecutor(max_workers=self.part_thread_num)
# 全部添加到任务队列开始处理
[thread_list.append(tp.submit(self.download_part, *args)) for args in pool_args_list]
# 等待所有线程结束,获取全部线程的执行结果
[multi_chunk_download_result.update(part_thread.result()) for part_thread in as_completed(thread_list)] # 下载总结
print('下载总结')
# 如果任务数和块儿数对不上,报一下出入
if len(multi_chunk_download_result) != chunk_count:
raise RuntimeError(
"%s part miss,expect=%d,actual=%d" % (finally_filename, chunk_count, len(multi_chunk_download_result)))
# 如果任务都完毕,检查是否有失败的块儿
for item in multi_chunk_download_result.keys():
if not multi_chunk_download_result[item]:
raise RuntimeError("%s part upload has fail" % item)
# 都OK 整合文件
with open(finally_filename, 'wb') as local_fd:
for i in range(chunk_count):
part_filename = finally_filename + '.' + str(i + 1)
with open(part_filename, 'rb') as part_fd:
while True:
bytes_data = part_fd.read(self.BUFFER_SIZE)
if not bytes_data:
break
local_fd.write(bytes_data) if length == os.stat(finally_filename).st_size:
print('%s 下载完成,文件大小相符' % finally_filename)
for part_filename in multi_chunk_download_result.keys():
os.remove(part_filename)
else:
print('%s 下载完成,但大小不符,content_length:%s 下载后大小 %s' % (finally_filename, length,os.stat(finally_filename).st_size )) if __name__ == '__main__':
downloader = DownLoader()
url = 'https://ks3-cn-beijing.ksyun.com/zhangmingda/111-3333333.Python安装与命令行操作.mp4'
print(url)
downloader.download(url)

urllib结合 concurrent.futures 多线程下载文件。的更多相关文章

  1. 多线程下载文件,ftp文件服务器

    1: 多线程下载文件 package com.li.multiplyThread; import org.apache.commons.lang3.exception.ExceptionUtils; ...

  2. java 网络编程基础 InetAddress类;URLDecoder和URLEncoder;URL和URLConnection;多线程下载文件示例

    什么是IPV4,什么是IPV6: IPv4使用32个二进制位在网络上创建单个唯一地址.IPv4地址由四个数字表示,用点分隔.每个数字都是十进制(以10为基底)表示的八位二进制(以2为基底)数字,例如: ...

  3. Python之FTP多线程下载文件之分块多线程文件合并

    Python之FTP多线程下载文件之分块多线程文件合并 欢迎大家阅读Python之FTP多线程下载系列之二:Python之FTP多线程下载文件之分块多线程文件合并,本系列的第一篇:Python之FTP ...

  4. Python之FTP多线程下载文件之多线程分块下载文件

    Python之FTP多线程下载文件之多线程分块下载文件 Python中的ftplib模块用于对FTP的相关操作,常见的如下载,上传等.使用python从FTP下载较大的文件时,往往比较耗时,如何提高从 ...

  5. 教你如何在 Android 使用多线程下载文件

    # 教你如何在 Android 使用多线程下载文件 前言 在 Android 日常开发中,我们会经常遇到下载文件需求,这里我们也可以用系统自带的 api DownloadManager 来解决这个问题 ...

  6. java 多线程下载文件 以及URLConnection和HttpURLConnection的区别

    使用 HttpURLConnection 实现多线程下载文件 注意GET大写//http public class MultiThreadDownload { public static void m ...

  7. java 多线程下载文件并实时计算下载百分比(断点续传)

    多线程下载文件 多线程同时下载文件即:在同一时间内通过多个线程对同一个请求地址发起多个请求,将需要下载的数据分割成多个部分,同时下载,每个线程只负责下载其中的一部分,最后将每一个线程下载的部分组装起来 ...

  8. AccessRandomFile多线程下载文件

    写一个工具类 package com.pb.thread.demo; import java.io.File; import java.io.FileNotFoundException; import ...

  9. WPF多线程下载文件,有进度条

    //打开对话框选择文件         private void OpenDialogBox_Click(object sender, RoutedEventArgs e)         {     ...

随机推荐

  1. 洛谷 P7078 - [CSP-S2020] 贪吃蛇(贪心)

    题面传送门 题意: 有 \(n\) 条蛇,每条蛇有个实力 \(a_i\) 我们称编号为 \(x\) 的蛇比编号为 \(y\) 的蛇强,当且仅当 \(a_x>a_y\) 或 \(a_x=a_y\) ...

  2. Topcoder 12519 ScotlandYard(点对 dp+最长路)

    题面传送门 题意: 有两个人 A 和 B 玩一个游戏.游戏规则大致是这样的: 有 \(n\) 个城市和三种交通工具公交.地铁和出租车. 给出三个 \(n\times n\) 的字符矩阵 \(b,m,t ...

  3. 洛谷 P3266 - [JLOI2015]骗我呢(容斥原理+组合数学)

    题面传送门 神仙题. 首先乍一看此题非常棘手,不过注意到有一个条件 \(0\le x_{i,j}\le m\),而整个矩阵恰好有 \(m\) 列,这就启发我们考虑将每个元素的上下界求出来,如果我们第一 ...

  4. python12对象初

  5. gcc 引用math 库 编译的问题 解决方法

    1.gcc app.c -lm 其中lm表示的是连接 m forlibm.so / libm.a表示你想要的库 abc for libabc.so / libabc.a 其中.a表示的是静态链接库 . ...

  6. 在前端页面中使用Markdown并且优化a标签

    近期在自己的项目中加入了对 Markdown 语法 的支持,主要用到的是markedjs这个项目.该项目托管在github上,地址为:https://github.com/markedjs/marke ...

  7. 如何在 ASP.NET Core 中构建轻量级服务

    在 ASP.NET Core 中处理 Web 应用程序时,我们可能经常希望构建轻量级服务,也就是没有模板或控制器类的服务. 轻量级服务可以降低资源消耗,而且能够提高性能.我们可以在 Startup 或 ...

  8. 【swift】长按事件绑定,平移滑动事件+坐标获取

    为何把这两个事件归类在一起? 我后来才明白,iOS有一个手势事件(UiGestureRecognizer) 事件里有7个功能,不过我只试过前两个,也就是标题的这两个(长按.平移滑动) UILongPr ...

  9. mysql explain using filesort

    创建表,字段tid上无索引(mysql 5.7) CREATE TABLE `test` ( `tid` int(11) DEFAULT NULL, `tname` varchar(12) DEFAU ...

  10. What happens when more restrictive access is given to a derived class method in C++?

    We have discussed a similar topic in Java here. Unlike Java, C++ allows to give more restrictive acc ...