使用NSURLConnection实现大文件断点下载

使用NSURLConnection实现大文件断点下载

由于是实现大文件的断点下载,不是下载一般图片什么的.在设计这个类的时候本身就不会考虑把下载的文件缓存到内存中,而是直接写到文件系统.

要实现断点下载,需要满足1个条件,那就是,必须要服务器支持断点下载.

 

实现的思路是这样子的:

1.  第一次会获取到被下载文件的总大小(服务器提供这个值)

  下载文件总大小 = 期望从服务器获取文件的大小 + 本地已经下载的文件的大小

2.  设置请求的缓存策略为不会读取本地中已经缓存的数据(NSURLRequestReloadIgnoringLocalCacheData)

3.  在去服务器请求数据之前先获取到本地已经下载好的部分文件的长度,以这个参数设置进Range中到服务器去请求剩下的数据

4.  当从网络获取到一定的数据的时候,我们直接将数据写进文件系统中

YXDownloadNetwork.h

//
//  YXDownloadNetwork.h
//  Download
//
//  http://home.cnblogs.com/u/YouXianMing/
//
//  Copyright (c) 2014年 Y.X. All rights reserved.
//

#import <Foundation/Foundation.h>

// block的相关定义
typedef void (^downloadProgress_t)(long long currentBytes, long long totalBytes);
typedef void (^completion_t)(NSDictionary *headers, NSData *body);

@interface YXDownloadNetwork : NSObject

// 将block定义成属性
@property (nonatomic, copy) downloadProgress_t       downloadProgress;
@property (nonatomic, copy) completion_t             completion;

// 初始化方法
- (instancetype)initWithUrlString:(NSString *)urlString cacheCapacity:(unsigned long long)capacity;
- (void)start;

@end

YXDownloadNetwork.m

//
//  YXDownloadNetwork.m
//  Download
//
//  http://home.cnblogs.com/u/YouXianMing/
//
//  Copyright (c) 2014年 Y.X. All rights reserved.
//

#import "YXDownloadNetwork.h"

@interface YXDownloadNetwork ()<NSURLConnectionDelegate, NSURLConnectionDataDelegate>

@property (nonatomic, assign) unsigned long long   totalLength;      // 文件总大小
@property (nonatomic, assign) unsigned long long   startDataLength;  // 本地存在文件的大小
@property (nonatomic, assign) unsigned long long   expectedLength;   // 从服务器期望文件的大小
@property (nonatomic, assign) unsigned long long   cacheCapacity;    // 缓存文件容量,以k为单位

@property (nonatomic, strong) NSURLConnection     *dataConncetion;   // 网络连接
@property (nonatomic, strong) NSDictionary        *responseHeaders;  // 网络连接头部信息
@property (nonatomic, strong) NSFileHandle        *file;             // 文件操作句柄
@property (nonatomic, strong) NSMutableData       *cacheData;        // 用于缓存的data数据

@end

@implementation YXDownloadNetwork

- (instancetype)initWithUrlString:(NSString *)urlString cacheCapacity:(unsigned long long)capacity
{
    self = [super init];

    if (self)
    {
        // 获取缓存容量
        if (capacity <= 0)
        {
            _cacheCapacity = 100 * 1024;
        }
        else
        {
            _cacheCapacity = capacity * 1024;
        }

        // 获取用于缓存的数据
        _cacheData = [NSMutableData new];

        // 获取文件名以及文件路径
        NSString *fileName = [urlString lastPathComponent];
        NSString *filePath = \
            fileFromPath([NSString stringWithFormat:@"/Documents/%@", fileName]);

        // 记录文件起始位置
        if ([[NSFileManager defaultManager] fileExistsAtPath:filePath])
        {
            // 从文件中读取出已经下载好的文件的长度
            _startDataLength = [[NSData dataWithContentsOfFile:filePath] length];
        }
        else
        {
            // 不存在则创建文件
            _startDataLength = 0;
            [[NSFileManager defaultManager] createFileAtPath:filePath
                                                    contents:nil
                                                  attributes:nil];
        }

        // 打开写文件流
        _file = [NSFileHandle fileHandleForWritingAtPath:filePath];

        // 创建一个网络请求
        NSMutableURLRequest* request = \
        [NSMutableURLRequest requestWithURL:[NSURL URLWithString:urlString]];

        // 禁止读取本地缓存
        [request setCachePolicy:NSURLRequestReloadIgnoringLocalCacheData];

        // 设置断点续传(需要服务器支持)
        [request setValue:[NSString stringWithFormat:@"bytes=%llu-", _startDataLength]
       forHTTPHeaderField:@"Range"];

        // 开始创建连接
        self.dataConncetion = \
        [[NSURLConnection alloc] initWithRequest:request
                                        delegate:self
                                startImmediately:NO];
    }

    return self;
}

- (void)start
{
    [self.dataConncetion start];
}

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
    if([response isKindOfClass:[NSHTTPURLResponse class]])
    {
        NSHTTPURLResponse *r = (NSHTTPURLResponse *)response;

        // 如果能获取到期望的数据长度就执行括号中的方法
        if ([r expectedContentLength] != NSURLResponseUnknownLength)
        {
            // 获取剩余要下载的
            _expectedLength  = [r expectedContentLength];

            // 计算出总共需要下载的
            _totalLength = _expectedLength + _startDataLength;

            // 获取头文件
            _responseHeaders = [r allHeaderFields];
        }
        else
        {
            NSLog(@"不支持断点下载");
        }
    }
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)theData
{
    // 追加缓存数据
    [_cacheData appendData:theData];

    // 如果该缓存数据的大小超过了指定的缓存大小
    if ([_cacheData length] >= _cacheCapacity)
    {
        // 移动到文件结尾
        [_file seekToEndOfFile];

        // 在文件末尾处追加数据
        [_file writeData:_cacheData];

        // 清空缓存数据
        [_cacheData setLength:0];
    }

    // 当前已经下载的所有数据的总量
    _startDataLength += [theData length];

    // 如果指定了block
    if (_downloadProgress)
    {
        _downloadProgress(_startDataLength, _totalLength);
    }
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
    // 移动到文件结尾
    [_file seekToEndOfFile];

    // 在文件末尾处追加最后的一点缓存数据
    [_file writeData:_cacheData];

    // 清空缓存
    [_cacheData setLength:0];

    NSLog(@"下载完成哦");
}

NS_INLINE NSString * fileFromPath(NSString *filePath)
{
    return [NSHomeDirectory() stringByAppendingString:filePath];
}

@end

测试代码如下:

实际上这个类还有很多地方不完善,但至少能起到抛砖引玉的作用,它更牛逼的用途靠你来修改了,亲.

时间: 2024-10-13 01:05:39

使用NSURLConnection实现大文件断点下载的相关文章

iOS开发之网络编程--使用NSURLConnection实现大文件断点续传下载

前言:iOS开发之网络编程--使用NSURLConnection实现大文件断点续传下载是在前篇iOS开发之网络编程--使用NSURLConnection实现大文件下载的基础上进行    断点续传的设置.关于iOS开发之网络编程--使用NSURLConnection实现大文件下载的细节这里当然就不会再累述的啦.   断点续传的原理:    每次在向服务器请求下载数据的同时,要告诉服务器从整个下载文件的数据流的某个还未下载的位置开始下载,然后服务器就返回从那个位置开始的数据流.    图片示意图:

java 通过apache ftp读取大文件或者下载大文件

问题描述 java 通过apache ftp读取大文件或者下载大文件 本人技术短,参照网上各位大侠的帖子写了登录ftp去读取ftp下面文件然后直接存进数据库的代码 ,但是我的代码只能读取一些小的文件,文件大点就报内存溢出.谁可以给个能在ftp上面下载大文件或者能够直接读取ftp服务器上面的大文件然后直接解析存进数据库的代码例子.不胜感激. 解决方案 内存溢出..说明内存方步下文件..ftp取到liu后写入文件吧...ps都内存溢出了..你不可能在内存中解析的..有可能是你jvm内存设置太小所致.

ios socket 断点上传-iOS怎么用socket实现大文件断点上传功能,类似于优酷中视频上传?

问题描述 iOS怎么用socket实现大文件断点上传功能,类似于优酷中视频上传? iOS怎么用socket实现大文件断点上传功能,类似于优酷中视频上传?哪位大神给点思路或者相关demo,跪求!!! 解决方案 Android中Socket大文件断点上传Android中Socket大文件断点上传Android中Socket大文件断点上传---------------------- 解决方案二: 一遍这种都是,两边定义好命令, 文件拆分成一块一块的,每一块有一个序号,断点续传就是没上传那块接着传就行了

MVC 实现大文件视频下载功能!

问题描述 求大神!解决C#MVC大文件视频下载功能.求源码! 解决方案 解决方案二:参考:

2345软件“大文件”软件下载攻略

  一.哪些是挂官方下载地址的大文件软件? 这类的软件和正常的软件是很容易区别开来的,拖动软件下载页至评论上方,有一个软件下载地址,普通软件会有很多的下载点,而大文件的软件只有一个官方下载地址.当然在页面中也会有"尊敬的用户,该软件非2345软件大全下载资源,下载后请注意检查"这样的提示(参照图2).两者对比如图1所示: 图1 二.应该怎样下载? 对于只有官方下载的软件来说,下面为大家总结几个下载方法. 方法一:在下载页面中直接点击立即下载,如图2所示: 图2 方法二:右键点击&quo

Android应用开发之使用Socket进行大文件断点上传续传

在Android中上传文件可以采用HTTP方式,也可以采用Socket方式,但是HTTP方式不能上传大文件,这里介绍一种通过Socket方式来进行断点续传的方式,服务端会记录下文件的上传进度,当某一次上传过程意外终止后,下一次可以继续上传,这里用到的其实还是J2SE里的知识. 这个上传程序的原理是:客户端第一次上传时向服务端发送"Content-Length=35;filename=WinRAR_3.90_SC.exe;sourceid="这种格式的字符串,服务端收到后会查找该文件是否

Android中Socket大文件断点上传

什么是Socket?           所谓Socket通常也称作"套接字",用于描述IP地址和端口,是一个通信连的句柄,应用程序通常通过"套接字"向网络发送请求或者应答网络请求,它就是网络通信过程中端点的抽象表示.它主要包括以下两个协议:       TCP (Transmission Control Protocol 传输控制协议):传输控制协议,提供的是面向连接.可靠的字节流服务.当客户和服务器彼此交换数据前,必须先在双方之间建立一个TCP连接,之后才能传输

Android中Socket大文件断点上传示例

什么是Socket? 所谓Socket通常也称作"套接字",用于描述IP地址和端口,是一个通信连的句柄,应用程序通常通过"套接字"向网络发送请求或者应答网络请求,它就是网络通信过程中端点的抽象表示.它主要包括以下两个协议: TCP (Transmission Control Protocol 传输控制协议):传输控制协议,提供的是面向连接.可靠的字节流服务.当客户和服务器彼此交换数据前,必须先在双方之间建立一个TCP连接,之后才能传输数据.TCP提供超时重发,丢弃重

Android中如何实现Socket大文件断点上传

什么是Socket? 所谓Socket通常也称作"套接字",用于描述IP地址和端口,是一个通信连的句柄,应用程序通常通过"套接字"向网络发送请求或者应答网络请求,它就是网络通信过程中端点的抽象表示.它主要包括以下两个协议: TCP (Transmission Control Protocol 传输控制协议):传输控制协议,提供的是面向连接.可靠的字节流服务.当客户和服务器彼此交换数据前,必须先在双方之间建立一个TCP连接,之后才能传输数据.TCP提供超时重发,丢弃重