zoukankan      html  css  js  c++  java
  • iOS开发中文件的上传和下载功能的基本实现-备用

    感谢大神分享

    这篇文章主要介绍了iOS开发中文件的上传和下载功能的基本实现,并且下载方面讲到了大文件的多线程断点下载,需要的朋友可以参考下

    文件的上传

    说明:文件上传使用的时POST请求,通常把要上传的数据保存在请求体中。本文介绍如何不借助第三方框架实现iOS开发中得文件上传。

      由于过程较为复杂,因此本文只贴出部分关键代码。

    主控制器的关键代码:

    复制代码代码如下:

    YYViewController.m
    #import "YYViewController.h"

    #define YYEncode(str) [str dataUsingEncoding:NSUTF8StringEncoding]

    @interface YYViewController ()

    @end


    复制代码代码如下:

    @implementation YYViewController

    - (void)viewDidLoad
    {
        [super viewDidLoad];
        // Do any additional setup after loading the view, typically from a nib.
    }

    - (void)upload:(NSString *)name filename:(NSString *)filename mimeType:(NSString *)mimeType data:(NSData *)data parmas:(NSDictionary *)params
    {
        // 文件上传
        NSURL *url = [NSURL URLWithString:@"http://192.168.1.200:8080/YYServer/upload"];
        NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
        request.HTTPMethod = @"POST";
        
        // 设置请求体
        NSMutableData *body = [NSMutableData data];
        
        /***************文件参数***************/
        // 参数开始的标志
        [body appendData:YYEncode(@"--YY ")];
        // name : 指定参数名(必须跟服务器端保持一致)
        // filename : 文件名
        NSString *disposition = [NSString stringWithFormat:@"Content-Disposition: form-data; name="%@"; filename="%@" ", name, filename];
        [body appendData:YYEncode(disposition)];
        NSString *type = [NSString stringWithFormat:@"Content-Type: %@ ", mimeType];
        [body appendData:YYEncode(type)];
        
        [body appendData:YYEncode(@" ")];
        [body appendData:data];
        [body appendData:YYEncode(@" ")];
        
        /***************普通参数***************/
        [params enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
            // 参数开始的标志
            [body appendData:YYEncode(@"--YY ")];
            NSString *disposition = [NSString stringWithFormat:@"Content-Disposition: form-data; name="%@" ", key];
            [body appendData:YYEncode(disposition)];

            [body appendData:YYEncode(@" ")];
            [body appendData:YYEncode(obj)];
            [body appendData:YYEncode(@" ")];
        }];
        
        /***************参数结束***************/
        // YY--
        [body appendData:YYEncode(@"--YY-- ")];
        request.HTTPBody = body;
        
        // 设置请求头
        // 请求体的长度
        [request setValue:[NSString stringWithFormat:@"%zd", body.length] forHTTPHeaderField:@"Content-Length"];
        // 声明这个POST请求是个文件上传
        [request setValue:@"multipart/form-data; boundary=YY" forHTTPHeaderField:@"Content-Type"];
        
        // 发送请求
        [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
            if (data) {
                NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:nil];
                NSLog(@"%@", dict);
            } else {
                NSLog(@"上传失败");
            }
        }];
    }

    - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
    {
        // Socket 实现断点上传
        
        //apache-tomcat-6.0.41/conf/web.xml 查找 文件的 mimeType
    //    UIImage *image = [UIImage imageNamed:@"test"];
    //    NSData *filedata = UIImagePNGRepresentation(image);
    //    [self upload:@"file" filename:@"test.png" mimeType:@"image/png" data:filedata parmas:@{@"username" : @"123"}];
        
        // 给本地文件发送一个请求
        NSURL *fileurl = [[NSBundle mainBundle] URLForResource:@"itcast.txt" withExtension:nil];
        NSURLRequest *request = [NSURLRequest requestWithURL:fileurl];
        NSURLResponse *repsonse = nil;
        NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&repsonse error:nil];
        
        // 得到mimeType
        NSLog(@"%@", repsonse.MIMEType);
        [self upload:@"file" filename:@"itcast.txt" mimeType:repsonse.MIMEType data:data parmas:@{
                                                                                                  @"username" : @"999",
                                                                                                  @"type" : @"XML"}];
    }

    @end


    补充说明:

    文件上传请求数据格式

    2015111091114601.png (654×353)

     

    部分文件的MIMEType

    2015111091158899.png (652×361)

     

      

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

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

    2015111091217169.png (305×260)

     

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

    2015111091236903.png (218×208)

    完成的实现代码如下:

    主控制器中的代码:

    复制代码代码如下:

    #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


    补充说明:如何获得将要下载的文件的大小?

    2015111091300364.png (802×205)

     

  • 相关阅读:
    (转)Too many open files
    Python小程序扫描清理Redis中的key
    spring-mvc接口返回json格式数据Long类型字段精度失真
    项目基础配置
    搭建Vue脚手架(vue-cli)并创建一个项目
    项目简介
    [技术学习]HTTP 常见状态码
    git 使用beyond compare 记录
    温故知新之架构图
    学年教学总结
  • 原文地址:https://www.cnblogs.com/isItOk/p/5305354.html
Copyright © 2011-2022 走看看