看到一篇多线程下载的文章,这里把自己的理解写一篇多线程下载的文章。

我们访问http://192.168.10.7/a.jpg时是get请求,response的head包含Content-Length: 37694

这个就是a.jpg文件的大小

抓包的话,server端是发送多个数据包(PDU)和一个文件信息,然后拼装成了a.jpg图片:

,部分截图。

如果我用requests.head("http://192.168.10.7/a.jpg")时,server端只返回文件信息,而不会发送文件数据。

 response = requests.head(self.url)print(response.headers)#
{'Keep-Alive': 'timeout=5, max=100', 'Accept-Ranges': 'bytes', 'Date': 'Sat, 18 Feb 2017 02:56:08 GMT', 'ETag': '"933e-548c4b0beff53"', 'Content-Type': 'image/jpeg', 'Content-Length': '37694', 'Last-Modified': 'S
at, 18 Feb 2017 02:21:39 GMT', 'Connection': 'Keep-Alive', 'Server': 'Apache/2.4.18 (Ubuntu)'}

文件a.jpg大小是37964字节

保存a.jpg文件后查看文件大小也是

好了,我们知道文件大小了的话,那如何多线程下载了?

假如我们用3个线程去下载a.jpg,那么我们会用线程1去下载1260x10=12600字节,线程2下载12601-25200字节,以此类推,还不够就用线程1再去下载。

但是get请求不是会直接下载a.jpg文件了?怎么只获取一部分文件的数据了?

我们可以在get请求的head部分加入“Range: bytes=0-12599”, 先测试下

# res.text 是将get获取的byte类型数据自动编码,是str类型, res.content是原始的byte类型数据# 所以下面是直接write(res.content)
        headers = {"Range":"bytes=0-12599"}res = requests.get(self.url,headers=headers)# res.text 是将get获取的byte类型数据自动编码,是str类型, res.content是原始的byte类型数据# 所以下面是直接write(res.content)with open(self.filename,'wb') as f:f.write(res.content)

然后可以看到下载获取的一部分图片:

我们再获取下一部分数据,

        headers = {"Range":"bytes=12600-25199"}res = requests.get(self.url,headers=headers)# res.text 是将get获取的byte类型数据自动编码,是str类型, res.content是原始的byte类型数据# 所以下面是直接write(res.content)with open(self.filename,'ab+') as f:print(f.tell())f.write(res.content)

可以看到文件:

我们知道:

r或rt 默认模式,文本模式读
rb   二进制文件w或wt 文本模式写,打开前文件存储被清空
wb  二进制写,文件存储同样被清空a  追加模式,只能写在文件末尾
a+ 可读写模式,写只能写在文件末尾w+ 可读写,与a+的区别是要清空文件内容
r+ 可读写,与a+的区别是可以写到文件任何位置

如果是多线程的而下载的话,我们用open('file','rb+'),我先用这种模式继续上面下载文件,上面下载到了25199字节,

那这次我从26000开始下载,f.seek(26000)后开始保存下载的文件,看文件是否能保存,看到的文件是否会中间出现空白:

        headers = {"Range":"bytes=26000-37694"}res = requests.get(self.url,headers=headers)# res.text 是将get获取的byte类型数据自动编码,是str类型, res.content是原始的byte类型数据# 所以下面是直接write(res.content)with open(self.filename,'rb+') as f:f.seek(26000)f.write(res.content)

下载后的文件:

这个,可能图片显示可能跟我们想象的不一样,但是rb+肯定是可以从任意位置读写的。

还介绍一个知识点,可能在自己测试的时候用的到,就是:

f.truncate(n):  从文件的首行首字符开始截断,截断文件为n个字符;无n表示从当前位置起截断;截断之后n后面的所有字符被删除。

好了,现在我们开始使用多线程下载文件:

设计思路是:

1、每个线程下载一部分数据

2、每个线程用rb+模式打开文件

3、每个线程下载数据后,用f.seek()到相应的位置,然后再写数据。

直接f=open(),再多线程f.write()时会出现文件写错误。

我们可以用os.dup()复制文件符合os.fsopen(fd,mode,buffer)来打开处理文件。

os.dup()和os.fdopen()的好处个人理解是os.dup()复制文件句柄,os.fdopen()先写缓存,具体官方文档还有待查证。

代码:

版本 python3,

pip install requests

下面代码可以拿来直接跑

#! -coding:utf8 -*-
import threading,sys
import requests
import time
import osclass MulThreadDownload(threading.Thread):def __init__(self,url,startpos,endpos,f):super(MulThreadDownload,self).__init__()self.url = urlself.startpos = startposself.endpos = endposself.fd = fdef download(self):print("start thread:%s at %s" % (self.getName(), time.time()))headers = {"Range":"bytes=%s-%s"%(self.startpos,self.endpos)}res = requests.get(self.url,headers=headers)# res.text 是将get获取的byte类型数据自动编码,是str类型, res.content是原始的byte类型数据# 所以下面是直接write(res.content)
        self.fd.seek(self.startpos)self.fd.write(res.content)print("stop thread:%s at %s" % (self.getName(), time.time()))# f.close()def run(self):self.download()if __name__ == "__main__":url = sys.argv[1]#获取文件的大小和文件名filename = url.split('/')[-1]filesize = int(requests.head(url).headers['Content-Length'])print("%s filesize:%s"%(filename,filesize))#线程数threadnum = 3#信号量,同时只允许3个线程运行
    threading.BoundedSemaphore(threadnum)# 默认3线程现在,也可以通过传参的方式设置线程数step = filesize // threadnummtd_list = []start = 0end = -1# 请空并生成文件tempf = open(filename,'w')tempf.close()# rb+ ,二进制打开,可任意位置读写with open(filename,'rb+') as  f:fileno = f.fileno()# 如果文件大小为11字节,那就是获取文件0-10的位置的数据。如果end = 10,说明数据已经获取完了。while end < filesize -1:start = end +1end = start + step -1if end > filesize:end = filesize# print("start:%s, end:%s"%(start,end))# 复制文件句柄dup = os.dup(fileno)# print(dup)# 打开文件fd = os.fdopen(dup,'rb+',-1)# print(fd)t = MulThreadDownload(url,start,end,fd)t.start()mtd_list.append(t)for i in  mtd_list:i.join()

执行结果:

python multiprocess_download.py http://192.168.10.7/of.tar.gz
of.tar.gz filesize:36578022
start thread:Thread-1 at 1487405833.7353075
start thread:Thread-2 at 1487405833.736311
start thread:Thread-3 at 1487405833.7378094
stop thread:Thread-1 at 1487405836.9561603
stop thread:Thread-3 at 1487405837.0016065
stop thread:Thread-2 at 1487405837.0116146

多次测试,下载后的文件都可以正常打开。

如果有多个站点有of.tar.gz文件,那更可以体现多线程下载的体验。

根据上面的理论,我们应该可以做一个类似p2p的下载,比如10台机器,每台启动一个agent,每个agent给server上报自己目录下的文件信息,当有一个agent有下载文件时,会去server查询哪些agent有这个文件,然后计算去哪些agent下载哪段数据。

转载于:https://www.cnblogs.com/owasp/p/6413480.html

python多线程下载文件相关推荐

  1. python多线程下载大文件_Python threading多线程断点下载文件的方法

    这是玩蛇网一篇关于Python多线程下载文件方法的代码实例.文中应用到的python模块和方法有httplib.Python urllib2.Python threading多线程模块.python ...

  2. python多线程下载小姐姐图片

    python多线程下载小姐姐图片 闲谈 思路 实现过程 单线程实现代码功能 问题描述 多线程处理办法 完成效果 闲谈 今日闲来无事,翻看博客,看到一篇关于python自动下载图片的文章,就萌生了也写一 ...

  3. Python 多线程下载图片

    多线程下载图片 参考链接: Python标准库-urllib和urllib3 urllib实战2–urllib基础urlretrieve().urlcleanup().info().getcode() ...

  4. python多线程读取文件的问题_Python多线程同步---文件读写控制方法

    1.实现文件读写的文件ltz_schedule_times.py #! /usr/bin/env python #coding=utf-8 import os def ReadTimes(): res ...

  5. python写http文件下载器_http分片请求-python分片下载文件

    源文件 http://theday.guohongfu.top/letter.txt内容为abcdefghijklmnopqrstuvwxyz 获取第20字节及以后的内容import requests ...

  6. python爬虫下载文件-【Py大法系列--03】Python如何自动下载文件

    问题描述 Python自动下载文件,通用文件,包括但不限于压缩文件.图片等. 解决方法 一般情况下,Python下载文件的方式有以下几种: 利用urllib.urllib2,即Python爬虫用的最多 ...

  7. python实现http下载文件-Python HTTP下载文件并显示下载进度条功能的实现

    下面的Python脚本中利用request下载文件并写入到文件系统,利用progressbar模块显示下载进度条. 其中利用request模块下载文件可以直接下载,不需要使用open方法,例如: im ...

  8. python下载方法-python实现下载文件的三种方法_python

    本篇文章主要介绍了python实现下载文件的三种方法,最常用的方法就是通过Http利用urllib或者urllib2模块还有requests,有兴趣的可以了解一下. Python开发中时长遇到要下载文 ...

  9. python下载的文件放在哪里的-python实现下载文件的三种方法

    Python开发中时长遇到要下载文件的情况,最常用的方法就是通过Http利用urllib或者urllib2模块. 当然你也可以利用ftplib从ftp站点下载文件.此外Python还提供了另外一种方法 ...

最新文章

  1. Maven项目整合讲义(Eclipse版)
  2. 【深度学习】同款商品识别的克星--ArcFace!
  3. Response_案例1_重定向_特点
  4. 柠檬汁制成的电池可以开动超100千克的车子吗?
  5. [Leedcode][JAVA][第105题][从前序与中序遍历序列构造二叉树][栈][递归][二叉树]
  6. 端口隔离配置命令、端口镜像(抓包配置)详解(附图,建议PC观看)
  7. java分库校验商户流水号是否重复,asp中用数据库生成不重复的流水号
  8. 基于SSM的宠物医院信息管理系统javaweb毕业设计项目源码论文
  9. 解决百度文库复制问题 非VIP也能复制文字
  10. Docker DeskTop安装Jenkins教程[Windows]
  11. 简单图片隐写术练习题
  12. oracle vm virtualbox安装xp系统,怎么使用VirtualBOX安装XP系统?VirtualBOX安装WinXp系统图文教程...
  13. torch的maximum与max以及导出onnx
  14. 计算机打字速度在线测试,在线测试打字速度(一分钟打字速度测试)
  15. 漫谈敏捷方法中的信任
  16. M-Arch(番外13)GD32L233评测-来点音乐
  17. 厉害了,分布式数据库中间件ShardingSphere毕业成为Apache顶级项目!
  18. linux vi 报错 E37: No write since last change (add to override)
  19. WordPress网站利用WP2PCS-SY插件定时、增量网站备份至百度网盘
  20. 如何去掉a标签的下划线

热门文章

  1. java getclass方法_JAVA-初步认识-第十一章-object类-Getclass方法
  2. Oracle中Null与空字符串''的区别
  3. 表里有索引,为什么还都是全表扫描?
  4. 一款好用的轮播插件swiper,适用于移动端和web
  5. 【转】Linux系统安装Redis详细过程
  6. WebSocket-java实现
  7. 直接法光度误差导数推导
  8. Binary Tree Non-recursive Traversal
  9. 获得指定时间所在月份的天数
  10. 循环语句练习题2(打印三角形和菱形)