也谈文件的多线程下载
来源:互联网 发布:医药b2b平台源码下载 编辑:程序博客网 时间:2024/04/29 19:55
如果是一个线程下载整个文件,我想,这谁都能够轻易的写出代码,但如果要求使用多线程进行下载,恐怕相当一部分人就无从下手了。所谓多线程,无非是每个线程只下载文件的一部分,最后将各个部分合并成一个文件。所以我们只要能解决以下几个问题就好了:
1、 获得文件的整体大小
2、 每个线程分配一个下载范围
3、 每个线程按照各自的下载范围进行下载
4、 最后合并各个部分
我们就按照上面的过程逐一进行拆解。首先,获得文件的大小,我查了一些网上流传的帖子,获得文件大小的方式都是直接向服务器发送一个get请求,然后根据响应头获得文件的大小。这里有一个小问题,那就是这个get请求发出去后,服务器发回来的可不只是响应头,还有文件流。但这一次请求根本不会去处理这个文件流,在我看来这是一种浪费,因为我们本可以用更好的办法,head请求。服务器收到head请求后,返回的只是响应头,文件实体部分不会返回,这样,就减少了不必要的浪费。
获得了文件的大小后,就可以轻松的进行分配了,比如有三个线程,那么就平均分配一下好了,比如文件大小为3000个字节,那么每个线程就下载1000个字节好了,如果是3002个字节,那剩余的两个字节分配给最后一个线程,三个线程的下载分配是1000,1000,1002。
前两步都完成了,最关键的是第三步,我想,这是很多人都表示为难的地方。其实呢,只要稍微对http协议了解一点的同学就能想到可以向服务器发起对文件的分段请求。比如,我就请求这个文件的第1000到第2000个字节的请求,服务器在收到请求后也会只发送文件的这一段。这里以python为例:
range = 'bytes=%s-%s' % (self.startindex,self.end) headers = {'Range':range} req = urllib2.Request(self.url, None,headers)
发出去的请求,明确的要求了请求文件数据的范围
最后一步合并,只需要等待3个线程运行结束就好了,我这里使用一个全局的Queue,每个线程完成下载后就往Queue里put一个数值,当Queue里的数值个数等于线程数时就是所有线程运行结束的时候
全部代码如下
#coding=utf-8'''Created on 2015-10-12先向服务器发送head请求,获得到文件的大小,然后起多个线程进行下载,最后合并@author: kwsy2015'''import Queueimport timeimport threadingimport urllib2import datetimeimport osq=Queue.Queue()class MyDownLoad(threading.Thread): def __init__(self,url,start,end,id,dir,filename): self.url = url self.startindex = start self.end = end self.id = id self.dir = dir self.filename = filename threading.Thread.__init__(self,name="producer Thread-%d" % id) def run(self): global q range = 'bytes=%s-%s' % (self.startindex,self.end) headers = {'Range':range} req = urllib2.Request(self.url, None,headers) f = urllib2.urlopen(req) self.filename = self.dir + self.filename self.filename = unicode(self.filename,'utf-8') with open(self.filename, "wb") as code: while True: data = f.read(1024) if not data: break code.write(data) q.put(1)def GetFileSize(url): request = urllib2.Request(url) request.get_method = lambda : 'HEAD' response = urllib2.urlopen(request) return int(response.info()['Content-Length'])def DownLoadFile(url,threadCount,dir): if threadCount<2: threadCount = 2 filename = url.split("/")[-1] filesize = GetFileSize(url) blocksize = filesize/threadCount for i in range(threadCount): start = i*blocksize if i is threadCount-1: end = filesize-1 else: end = (i+1)*blocksize-1 id = i tmpfilename = '%s-%d' % (filename,i) print tmpfilename dlthread = MyDownLoad(url,start,end,id,dir,tmpfilename) dlthread.start()def Merge(dir,name): dir = unicode(dir,'utf-8') lst = [] for parent,dirnames,filenames in os.walk(dir): for filename in filenames: fullname = os.path.join(parent,filename) fullname = fullname.encode("utf-8") lst.append(fullname) file = open(os.path.join(dir,name),'wb') filename = lst[1].split("-")[-2] count = len(lst) for i in range(count-1): mergefile = '%s-%d' % (filename,i) mergefile = unicode(mergefile,'utf-8') print mergefile mfile = open(mergefile,'rb') while True: data = mfile.read(1024) if not data: break file.write(data) mfile.close() file.close() if __name__ == '__main__': starttime = datetime.datetime.now() DownLoadFile('http://mn2.pc6.com/rm/python2.7.zip',3,'f:/测试/') while not q.qsize() is 3: pass Merge('f:/测试/','python2.7.zip') endtime = datetime.datetime.now() print (endtime - starttime).seconds
- 也谈文件的多线程下载
- 文件的多线程下载
- 采用多线程的方式实现文件下载
- 多线程下载百度分享的大文件
- 大文件的多线程断点下载
- 多线程断点下载文件的需求
- C#实现多线程下载文件的方法
- 28.java多线程实现文件的下载
- Http多线程下载文件的处理机制
- 多线程文件下载的服务器端及客户端
- 加入多线程的文件下载程序
- 打开网上下载的CHM文件什么内容也看不到
- 也谈下载Cygwin的安装包
- 多线程技术下载文件
- 多线程下载文件
- 使用多线程下载文件
- Android 多线程文件下载
- 多线程文件下载
- Magento查询历史完成订单的产品销售价格
- poj 1321
- 谈ubuntu12.04 64bit安装hadoop2.2.0的方法
- Jquery实现表单的动态验证
- 网易javascript解答
- 也谈文件的多线程下载
- SQL MID() 函数
- 一位前辈的作品 - MMPopupView(弹出框组件
- Django学习指南之Model层
- java中的io——读写大文件(二)
- SQL LEN() 函数
- S5PV210的busybox移植
- 进击的KFC:第六节:C语言:函数
- 【C++设计技巧】C++中的RAII机制 http://www.cnblogs.com/gnuhpc/archive/2012/12/04/2802307.html