听说有人面试的时候,遇到过这个问题。开始以为是开个线程下载文件,了解后才明白是开多才线程下载同一个文件。就如同迅雷。
但是并不建议这样去做:
1.因为线程开多了,并不会提高效率,反而会影响效率。CPU在线程中切换调度的问题。
2.每开启一条线程,都会有资源消耗,移动端目前是没办法像PC那样可以任其挥霍的。
3.耗电,很耗电,非常的耗电。想想你的手机用了没多久,看着电量一截一截的掉,你是不是慌的一B。现在年轻人最崩溃的是啥?!没电,早起,没wifi!!!!
进入正题
开启一条线程下载一个文件,没难度。
开启一条线程下载断点续传,难度不大。(NSURLConnection 和NSURLSession2种都可以实现断点续传,NSURLSession更加方便点)
那么开启多个线程同一个文件,有一点点难度,但是也不算太大。思路就是,断点续传。今天就以NSURLConnection为列子。
NSURLConnection的断点续传是,在下载的时候设置Range。
NSString *range = @"bytes=1000-2000";
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setValue:Range forHTTPHeaderField:@"Range"];
上面设置请求头以后,那么就只会下载url中的1000-2000这一段的数据。
所以,我们可以把要下载的文件分成N个,如何分看需求。以一个固定的大小来分?!还是获取服务器文件大小以后,按等分来分?!按需选择。
然后按划分的个数,来创建线程,在每个线程中创建一个NSURLConnection并设置对应的Range。为了内存不要占用过大(太大可能会崩溃),所以在下载的时候,下载多少数据,我们就写入多少。用NSOutputStream写入文件。不同的分段,写入文件的地址最好区别开来。
然后在所有分段下载完成后,在使用NSFileHandle来把所有文件合成最后我们需要的文件。写入一个分段,删除那个分段。思路就是这样,下面说几个需要注意的地方。
因为我们支持断点续传,那么在设置Range的时候,我们每次都需要获取本地已下载的大小,然后去匹配,
如果本地文件大小与分配的大小相同,那么就不需要下载。
如果本地文件大小小于分配的大小,那么需要重新设置Range,然后进行下载(为了断点续传)。
如果本地文件大小大于分配的大小,那么删除,重新下载。
今天的代码,很多也是基于下载管理器来修改的。有疑问可以看看前面的下载管理器。
上面的MD5是代码断点续传下载的,后面是用游览器下载的。最后的MD5是一样的。证明下载后的文件是OK的。不存在打不开!!!!
下载 只是简单的实现了这个功能并提出来做成了一个manager,其实还可以继续封装完善,比如分割的模式,进度的回调(偷懒,没写完)等等。