iOS开发网络篇—实现大文件的多线程断点下载

说明:本文介绍多线程断点下载。项目中使用了苹果自带的类,实现了同时开启多条线程下载一个较大的文件。因为实现过程较为复杂,所以下面贴出完整的代码。

实现思路:下载开始,创建一个和要下载的文件大小相同的文件(如果要下载的文件为100M,那么就在沙盒中创建一个100M的文件,然后计算每一段的下载量,开启多条线程下载各段的数据,分别写入对应的文件部分)。

项目中用到的主要类如下:

完成的实现代码如下:

主控制器中的代码:

#import "YYViewController.h"
#import "YYFileMultiDownloader.h"

@interface YYViewController ()
@property (nonatomic, strong) YYFileMultiDownloader *fileMultiDownloader;
@end

@implementation YYViewController
- (YYFileMultiDownloader *)fileMultiDownloader
{
  if (!_fileMultiDownloader) {
    _fileMultiDownloader = [[YYFileMultiDownloader alloc] init];
    // 需要下载的文件远程URL
    _fileMultiDownloader.url = @"http://192.168.1.200:8080/MJServer/resources/jre.zip";
    // 文件保存到什么地方
    NSString *caches = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
    NSString *filepath = [caches stringByAppendingPathComponent:@"jre.zip"];
    _fileMultiDownloader.destPath = filepath;
  }
  return _fileMultiDownloader;
}

- (void)viewDidLoad
{
  [super viewDidLoad];

}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
  [self.fileMultiDownloader start];
}

@end

自定义一个基类

YYFileDownloader.h文件

#import <Foundation/Foundation.h>

@interface YYFileDownloader : NSObject
{
  BOOL _downloading;
}
/**
 * 所需要下载文件的远程URL(连接服务器的路径)
 */
@property (nonatomic, copy) NSString *url;
/**
 * 文件的存储路径(文件下载到什么地方)
 */
@property (nonatomic, copy) NSString *destPath;

/**
 * 是否正在下载(有没有在下载, 只有下载器内部才知道)
 */
@property (nonatomic, readonly, getter = isDownloading) BOOL downloading;

/**
 * 用来监听下载进度
 */
@property (nonatomic, copy) void (^progressHandler)(double progress);

/**
 * 开始(恢复)下载
 */
- (void)start;

/**
 * 暂停下载
 */
- (void)pause;
@end

YYFileDownloader.m文件

#import "YYFileDownloader.h"

@implementation YYFileDownloader
@end
下载器类继承自YYFileDownloader这个类

YYFileSingDownloader.h文件
#import "YYFileDownloader.h"

@interface YYFileSingleDownloader : YYFileDownloader
/**
 * 开始的位置
 */
@property (nonatomic, assign) long long begin;
/**
 * 结束的位置
 */
@property (nonatomic, assign) long long end;
@end
YYFileSingDownloader.m文件
#import "YYFileSingleDownloader.h"
@interface YYFileSingleDownloader() <NSURLConnectionDataDelegate>
/**
 * 连接对象
 */
@property (nonatomic, strong) NSURLConnection *conn;

/**
 * 写数据的文件句柄
 */
@property (nonatomic, strong) NSFileHandle *writeHandle;
/**
 * 当前已下载数据的长度
 */
@property (nonatomic, assign) long long currentLength;
@end

@implementation YYFileSingleDownloader

- (NSFileHandle *)writeHandle
{
  if (!_writeHandle) {
    _writeHandle = [NSFileHandle fileHandleForWritingAtPath:self.destPath];
  }
  return _writeHandle;
}

/**
 * 开始(恢复)下载
 */
- (void)start
{
  NSURL *url = [NSURL URLWithString:self.url];
  // 默认就是GET请求
  NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
  // 设置请求头信息
  NSString *value = [NSString stringWithFormat:@"bytes=%lld-%lld", self.begin + self.currentLength, self.end];
  [request setValue:value forHTTPHeaderField:@"Range"];
  self.conn = [NSURLConnection connectionWithRequest:request delegate:self];

  _downloading = YES;
}

/**
 * 暂停下载
 */
- (void)pause
{
  [self.conn cancel];
  self.conn = nil;

  _downloading = NO;
}

#pragma mark - NSURLConnectionDataDelegate 代理方法
/**
 * 1. 当接受到服务器的响应(连通了服务器)就会调用
 */
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{

}

/**
 * 2. 当接受到服务器的数据就会调用(可能会被调用多次, 每次调用只会传递部分数据)
 */
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
  // 移动到文件的尾部
  [self.writeHandle seekToFileOffset:self.begin + self.currentLength];
  // 从当前移动的位置(文件尾部)开始写入数据
  [self.writeHandle writeData:data];

  // 累加长度
  self.currentLength += data.length;

  // 打印下载进度
  double progress = (double)self.currentLength / (self.end - self.begin);
  if (self.progressHandler) {
    self.progressHandler(progress);
  }
}

/**
 * 3. 当服务器的数据接受完毕后就会调用
 */
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
  // 清空属性值
  self.currentLength = 0;

  // 关闭连接(不再输入数据到文件中)
  [self.writeHandle closeFile];
  self.writeHandle = nil;
}

/**
 * 请求错误(失败)的时候调用(请求超时\断网\没有网, 一般指客户端错误)
 */
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{

}

@end

设计多线程下载器(利用HMFileMultiDownloader能开启多个线程同时下载一个文件)

一个多线程下载器只下载一个文件

YYFileMultiDownloader.h文件

#import "YYFileDownloader.h"
@interface YYFileMultiDownloader : YYFileDownloader
@end

YYFileMultiDownloader.m文件

#import "YYFileMultiDownloader.h"
#import "YYFileSingleDownloader.h"

#define YYMaxDownloadCount 4

@interface YYFileMultiDownloader()
@property (nonatomic, strong) NSMutableArray *singleDownloaders;
@property (nonatomic, assign) long long totalLength;
@end

@implementation YYFileMultiDownloader

- (void)getFilesize
{
  NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:self.url]];
  request.HTTPMethod = @"HEAD";

  NSURLResponse *response = nil;
#warning 这里要用异步请求
  [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:nil];
  self.totalLength = response.expectedContentLength;
}

- (NSMutableArray *)singleDownloaders
{
  if (!_singleDownloaders) {
    _singleDownloaders = [NSMutableArray array];

    // 获得文件大小
    [self getFilesize];

    // 每条路径的下载量
    long long size = 0;
    if (self.totalLength % YYMaxDownloadCount == 0) {
      size = self.totalLength / YYMaxDownloadCount;
    } else {
      size = self.totalLength / YYMaxDownloadCount + 1;
    }

    // 创建N个下载器
    for (int i = 0; i<YYMaxDownloadCount; i++) {
      YYFileSingleDownloader *singleDownloader = [[YYFileSingleDownloader alloc] init];
      singleDownloader.url = self.url;
      singleDownloader.destPath = self.destPath;
      singleDownloader.begin = i * size;
      singleDownloader.end = singleDownloader.begin + size - 1;
      singleDownloader.progressHandler = ^(double progress){
        NSLog(@"%d --- %f", i, progress);
      };
      [_singleDownloaders addObject:singleDownloader];
    }

    // 创建一个跟服务器文件等大小的临时文件
    [[NSFileManager defaultManager] createFileAtPath:self.destPath contents:nil attributes:nil];

    // 让self.destPath文件的长度是self.totalLengt
    NSFileHandle *handle = [NSFileHandle fileHandleForWritingAtPath:self.destPath];
    [handle truncateFileAtOffset:self.totalLength];
  }
  return _singleDownloaders;
}

/**
 * 开始(恢复)下载
 */
- (void)start
{
  [self.singleDownloaders makeObjectsPerformSelector:@selector(start)];

  _downloading = YES;
}

/**
 * 暂停下载
 */
- (void)pause
{
  [self.singleDownloaders makeObjectsPerformSelector:@selector(pause)];
  _downloading = NO;
}

@end

补充说明:如何获得将要下载的文件的大小?
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • 在IOS中为什么使用多线程及多线程实现的三种方法

    多线程是一个比较轻量级的方法来实现单个应用程序内多个代码执行路径. 在系统级别内,程序并排执行,程序分配到每个程序的执行时间是基于该程序的所需时间和其他程序的所需时间来决定的. 然而,在每个程序内部,存在一个或者多个执行线程,它同时或在一个几乎同时发生的方式里执行不同的任务. 概要提示: iPhone中的线程应用并不是无节制的,官方给出的资料显示,iPhone OS下的主线程的堆栈大小是1M,第二个线程开始就是512KB,并且该值不能通过编译器开关或线程API函数来更改,只有主线程有直接修改UI

  • IOS 线程死锁详细介绍

    iOS线程死锁 前言: 在chat view的开发过程中,添加了"混合标签添加与显示",app出现发送图片会出现卡死的情况,但过了大约30-40 second后会恢复正常. 问题分析: 因为没有任何报错与提示,只能根据表面现象慢慢分析,经过多次测试与观察得出以下规律: (1)发送表情与文本不会发生该情况,只有发送图片才会发生app界面卡死的情况.(主线程阻塞,与大文件上传有关)      (2)app卡死一定时间后会恢复正常,但时间不定,大约范围在30-40 second.(主线程解除

  • 详解iOS多线程之2.NSThread的加锁@synchronized

    那什么时候需要加锁呢,就是当多条线程同时操作一个变量时,就需要加锁了. 上代码 声明变量 @interface ViewController () @property (strong, nonatomic)NSThread *thread1; @property (strong, nonatomic)NSThread *thread2; @property (strong, nonatomic)NSThread *thread3; @property (assign, nonatomic)int

  • IOS 创建并发线程的实例详解

    IOS 创建并发线程的实例详解 创建并发线程 主线程一般都是处理UI界面及用户交互的事儿的.其他的事一般就要另外的线程去处理,如下载,计算等... 现在先简单创建3个线程,分别打印出1-1000,,为了方便,线程3就放在主线程中执行. - (void) firstCounter{ @autoreleasepool { NSUInteger counter = 0; for (counter = 0; counter < 1000; counter++){ NSLog(@"First Cou

  • IOS多线程实现多图片下载(一)

    在没有步入正文之前先给大家展示下效果图,如果大家觉得很满意请继续往下阅读全文. 大家可以看到这个界面很简单,其实就是UITableView的布局,但是难点是在于如何从网上下载这些图片,下载之后应如何进行存储! 我们一步一步进行解析,先从单线程(主线程)进行多图片下载我们布局上的文字及图片的地址从plist文件中进行读取 根据结构,我们自定义一个数据模型文件 DDZApp.h #import <Foundation/Foundation.h> @interface DDZApp : NSObje

  • 详解iOS中多线程app开发的GCD队列的使用

    GCD的基本使用 一.主队列介绍 主队列:是和主线程相关联的队列,主队列是GCD自带的一种特殊的串行队列,放在主队列中得任务,都会放到主线程中执行. 提示:如果把任务放到主队列中进行处理,那么不论处理函数是异步的还是同步的都不会开启新的线程. 获取主队列的方式: 复制代码 代码如下: dispatch_queue_t queue=dispatch_get_main_queue(); (1)使用异步函数执行主队列中得任务,代码示例: 复制代码 代码如下: // //  YYViewControll

  • 详解iOS 多线程 锁 互斥 同步

    在iOS中有几种方法来解决多线程访问同一个内存地址的互斥同步问题: 方法一,@synchronized(id anObject),(最简单的方法) 会自动对参数对象加锁,保证临界区内的代码线程安全 @synchronized(self) { // 这段代码对其他 @synchronized(self) 都是互斥的 // self 指向同一个对象 } 方法二,NSLock NSLock对象实现了NSLocking protocol,包含几个方法: lock,加锁 unlock,解锁 tryLock

  • iOS开发网络篇—实现大文件的多线程断点下载

    说明:本文介绍多线程断点下载.项目中使用了苹果自带的类,实现了同时开启多条线程下载一个较大的文件.因为实现过程较为复杂,所以下面贴出完整的代码. 实现思路:下载开始,创建一个和要下载的文件大小相同的文件(如果要下载的文件为100M,那么就在沙盒中创建一个100M的文件,然后计算每一段的下载量,开启多条线程下载各段的数据,分别写入对应的文件部分). 项目中用到的主要类如下: 完成的实现代码如下: 主控制器中的代码: #import "YYViewController.h" #import

  • IOS开发网络篇—Socket编程详解

    一.网络各个协议:TCP/IP.SOCKET.HTTP等 网络七层由下往上分别为物理层.数据链路层.网络层.传输层.会话层.表示层和应用层. 其中物理层.数据链路层和网络层通常被称作媒体层,是网络工程师所研究的对象: 传输层.会话层.表示层和应用层则被称作主机层,是用户所面向和关心的内容. http协议对应于应用层 tcp协议对应于传输层 ip协议对应于网络层 三者本质上没有可比性.  何况HTTP协议是基于TCP连接的. TCP/IP是传输层协议,主要解决数据如何在网络中传输:而HTTP是应用

  • iOS开发实现HTTPS之cer文件的使用详解

    iOS开发中实现支持HTTPS,有两种方法:一是后台那边都处理好了,移动端直接可以使用HTTPS接口,二是后台给移动端一个服务器证书cer 文件,这时我们就需要将cer文件导入到我们的工程中,以下是实现方法 1. 双击证书,这时证书已经添加到了钥匙串中 2. 将cer 文件拖入工程中 3. 如果使用的是AFNetwotking 的话,在代码中添加以下代码 AFHTTPSessionManager *manager = [AFHTTPSessionManager manager]; //证书 AF

  • webuploader在springMVC+jquery+Java开发环境下的大文件分片上传的实例代码

    注意: 1,webuploader上传组件会和jQuery自带的上传组件冲突,所以不要使用<form>标签中添加上传文件的属性; enctype="multipart/form-data" 2.并且屏蔽ApplicationContext-mvc.xml里面的拦截配置! <!-- 上传拦截,如最大上传值及最小上传值 --> <!--新增加的webuploader上传组件,必须要屏蔽这里的拦截机制 <bean id="multipartRes

  • 前端大文件上传与下载(分片上传)的详细过程

    目录 一.问题 二.解决 1.第一步选择文件 2.校验文件是否符合规范 3.文件切片上传 4.分片上传注意点 5.大文件下载 总结 一.问题 日常业务中难免出现前端需要向后端传输大型文件的情况,这时单次的请求不能满足传输大文件的需求,就需要用到分片上传 业务需求为:用户可以上传小于20G的镜像文件,并进显示当前上传进度 前端:vue3.x+Element Plus组件+axios 二.解决 解决思路简单为前端选择文件后读取到文件的基本信息,包括:文件的大小.文件格式等信息,用于前端校验,校验完成

  • iOS开发网络编程之断点续传

    前言 网络下载是我们在项目中经常要用到的功能,如果是小文件的下载,比如图片和文字之类的,我们可以直接请求源地址,然后一次下载完毕.但是如果是下载较大的音频和视频文件,不可能一次下载完毕,用户可能下载一段时间,关闭程序,回家接着下载.这个时候,就需要实现断点续传的功能.让用户可以随时暂停下载,下次开始下载,还能接着上次的下载的进度. 今天我们来看看如何自己简单的封装一个断点续传的类,实现如下功能. 1.使用者只需要调用一个接口即可以下载,同时可以获取下载的进度. 2.下载成功,可以获取文件存储的位

  • iOS开发UI篇—xib的简单使用实例

    这个博客申请了有一段时间了,觉得好像是该写点什么了.这篇文章主要是关于一些xib的简单的用法,希望可以帮助到刚刚使用xib的新手们. 什么是xib? xib能做什么? 用来描述软件界面的文件. 如果没有xib,所有的界面都需要通过代码来手动创建. 有了xib以后,可以在xib中进行可视化开发,然后加载xib文件的时候,系统自动生成对应的代码来创建界面. 与xib类似的还有storyboard文件.xib和storyboard的比较,一个轻量级一个重量级. 共同点: 都用来描述软件界面.都用Int

  • 使用java实现http多线程断点下载文件(一)

    基本原理:利用URLConnection获取要下载文件的长度.头部等相关信息,并设置响应的头部信息.并且通过URLConnection获取输入流,将文件分成指定的块,每一块单独开辟一个线程完成数据的读取.写入.通过输入流读取下载文件的信息,然后将读取的信息用RandomAccessFile随机写入到本地文件中.同时,每个线程写入的数据都文件指针也就是写入数据的长度,需要保存在一个临时文件中.这样当本次下载没有完成的时候,下次下载的时候就从这个文件中读取上一次下载的文件长度,然后继续接着上一次的位

  • 使用java实现http多线程断点下载文件(二)

    下载工具我想没有几个人不会用的吧,前段时间比较无聊,花了点时间用java写了个简单的http多线程下载程序,纯粹是无聊才写的,只实现了几个简单的功能,而且也没写界面,今天正好也是一个无聊日,就拿来写篇文章,班门弄斧一下,觉得好给个掌声,不好也不要喷,谢谢! 我实现的这个http下载工具功能很简单,就是一个多线程以及一个断点恢复,当然下载是必不可少的.那么大概先整理一下要做的事情: 1.连接资源服务器,获取资源信息,创建文件 2.切分资源,多线程下载 3.断点恢复功能 4.下载速率统计 大概就这几

  • 关于SpringBoot大文件RestTemplate下载解决方案

    近期基于项目上使用到的RestTemplate下载文件流,遇到1G以上的大文件,下载需要3-4分钟,因为调用API接口没有做分片与多线程, 文件流全部采用同步方式加载,性能很慢.最近结合网上案例及自己总结,写了一个分片下载tuling/fileServer项目: 1.包含同步下载文件流在浏览器加载输出相关代码: 2.包含分片多线程下载分片文件及合并文件相关代码: 另外在DownloadThread项目中使用代码完成了一个远程RestUrl请求去获取一个远端资源大文件进行多线程分片下载 到本地的一

随机推荐