热门标签 | HotTags
当前位置:  开发笔记 > 前端 > 正文

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\r\n")];
    // name : 指定参数名(必须跟服务器端保持一致)
    // filename : 文件名
    NSString *disposition = [NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"\r\n", name, filename];
    [body appendData:YYEncode(disposition)];
    NSString *type = [NSString stringWithFormat:@"Content-Type: %@\r\n", mimeType];
    [body appendData:YYEncode(type)];
   
    [body appendData:YYEncode(@"\r\n")];
    [body appendData:data];
    [body appendData:YYEncode(@"\r\n")];
   
    /***************普通参数***************/
    [params enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
        // 参数开始的标志
        [body appendData:YYEncode(@"--YY\r\n")];
        NSString *disposition = [NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n", key];
        [body appendData:YYEncode(disposition)];

        [body appendData:YYEncode(@"\r\n")];
        [body appendData:YYEncode(obj)];
        [body appendData:YYEncode(@"\r\n")];
    }];
   
    /***************参数结束***************/
    // YY--\r\n
    [body appendData:YYEncode(@"--YY--\r\n")];
    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

@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()
/**
 * 连接对象
 */
@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             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)


推荐阅读
  • ServletContext接口在Java Web开发中扮演着重要角色,它提供了一种方式来获取关于整个Web应用程序的信息。通过ServletContext,开发者可以访问初始化参数、共享数据以及应用资源。 ... [详细]
  • Java EE CDI:解决依赖关系冲突的实例
    在本教程中,我们将探讨如何在Java EE的CDI(上下文和依赖注入)框架中有效解决依赖关系的冲突问题。通过学习如何使用限定符,您将能够为应用程序的不同客户端提供多种接口实现,并确保每个客户端都能正确调用其所需的实现。 ... [详细]
  • 在Linux系统上构建Web服务器的详细步骤
    本文详细介绍了如何在Linux系统上搭建Web服务器的过程,包括安装Apache、PHP和MySQL等关键组件,以及遇到的一些常见问题及其解决方案。 ... [详细]
  • 本文探讨了Web开发与游戏开发之间的主要区别,旨在帮助开发者更好地理解两种开发领域的特性和需求。文章基于作者的实际经验和网络资料整理而成。 ... [详细]
  • 本文将详细介绍如何在ThinkPHP6框架中实现多数据库的部署,包括读写分离的策略,以及如何通过负载均衡和MySQL同步技术优化数据库性能。 ... [详细]
  • 本文详细介绍了Linux操作系统中的cp和scp命令,包括它们的基本使用方法、常见选项以及如何通过scp命令安全地在不同主机之间传输文件。 ... [详细]
  • 本文探讨如何使用 PHP 进行字符串处理,特别是如何检测一个字符串是否存在于另一个字符串中,并确定其具体位置。通过实例代码展示,帮助读者掌握这一常用功能。 ... [详细]
  • APM(Application Performance Management,应用性能管理)对于提供互联网服务的企业至关重要。本文将深入探讨APM如何帮助识别和解决导致用户流失的技术问题,以及它在提升整体用户体验方面的作用。 ... [详细]
  • Redis安全防护深入解析
    本文详细探讨了如何通过指令安全、端口管理和SSL代理等措施有效保护Redis服务的安全性。 ... [详细]
  • JavaWeb技术架构解析
    本文探讨了JavaWeb开发中客户端与服务器端的交互模式,重点分析了B/S(浏览器/服务器)和C/S(客户端/服务器)两种架构的特点及应用场景。 ... [详细]
  • 深入分析十大PHP开发框架
    随着PHP技术的发展,各类开发框架层出不穷,成为了开发者们热议的话题。本文将详细介绍并对比十款主流的PHP开发框架,旨在帮助开发者根据自身需求选择最合适的工具。 ... [详细]
  • 性能测试工具的选择与应用
    本文探讨了性能测试工具的重要性及其在软件测试中的作用,重点介绍了选择合适性能测试工具的考量因素,并对几种常用的性能测试工具进行了对比分析。 ... [详细]
  • 全能终端工具推荐:高效、免费、易用
    介绍一款备受好评的全能型终端工具——MobaXterm,它不仅功能强大,而且完全免费,适合各类用户使用。 ... [详细]
  • 本文将详细探讨 Linux 系统中的 netstat 命令,该命令用于查看网络状态和连接情况。通过了解 IP 地址和端口的基本概念,我们将更好地理解如何利用 netstat 命令来监控和管理网络服务。 ... [详细]
  • 详解Linux系统启动过程及/etc/rc.d与/etc/rc.d/init.d目录关系
    本文深入探讨了Linux系统启动流程、运行级别及其与/etc/rc.d和/etc/rc.d/init.d目录的关联,旨在帮助读者理解系统启动时各脚本和服务的加载机制。 ... [详细]
author-avatar
雨蝶馨菲_484
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有