iOS开发 FTP使用详解
2014-12-21 11:57
483 查看
网络上大多数的文件的上传都是通过ASIHttpRequest库以form表单的形式完成的,比较简单,代码逻辑也比较清晰,在此笔者就不赘述了。而今天要跟大家分享的是在iOS端使用ftp的形式进行上传的方式。
由于网上关于在iOS端ftp上传的资料很少,而且刚好笔者也需要用到ftp,因此,便促成了此文的诞生。
在iOS端的ftp上传使用的是CFNetWork框架,它处于核心服务框架中,提供了一个抽象化的网络协议库。使用CFNetWork框架中的CFFTPStream类提供的API能够做很多ftp操作,例如上传文件、下载文件、下载目录列表,创建远程目录等等。
但是在使用上也存在一些的限制,CFFTPStream 提供的ftp操作比较基础,它并不是完全的FTP客户端API,不支持所有的FTP操作。
例如:
FTPS(FTP over TLS)
删除文件(没法像Android里面的comment-net包那样可以直接删除服务器文件)
重命名文件
自定义FTP命令
另外,CFFTPStream的目录解析也局限于简单的目录,只能够解析一般的基于UNIX FTP服务的目录。
要使用CFFTPStream的功能,需要导入CFNetWork 框架,方便的是不用去下载第三方包,直接在Xcode里面添加就好了。
第一步:
第二步:
建立SingleView的项目,配置好nib文件。包括以下控件:
文件路径输入框
服务器路径输入框
账号输入框
密码输入框
传输状态显示
上传按钮
view
sourceprint?
使用CFFTPStream进行上传,首先要遵循NSStreamDelegate协议。并实现协议中的委托方法。
下面为NSStreamDelegate协议的内容。
@protocol NSStreamDelegate <NSObject>
@optional
- (void)stream:(NSStream *)aStreamhandleEvent:(NSStreamEvent)eventCode;
@end
定义相关内部变量与存取方法:
view
sourceprint?
size_t与uint8_t为c扩展的类型变量,在用于跨语言的时候比较方便,uint8_t为一个字节大小的无符号int类型,size_t在32位与64位机器上代表不同的大小,分别为4个字节与8个字节。
因为buffer被声明为一个数组,所以必须要使用自定义的getter方法,synthesised方法是不会被编译的。
点击上传按钮,并生成networkStream流
view
sourceprint?
实现代理方法:
view
sourceprint?
bytesWritten为实际写入的数据量,虽然缓冲区大小是固定的值,但每次写入并不一定是充满缓冲区,可以看到NSLog的输出值。(上传图片大小为428492字节,约430k)。
首先是NSStreamEventOpenCompleted打开事件完成的回调,然后不断发送NSStreamEventHasSpaceAvailable消息完成整个上传过程。
可以看到在传输的时候,bufferOffset并不是每次都是32768,所以self.bufferOffset!= self.bufferLimit是一个续传上次未完数据的过程,而当self.bufferOffset = self.bufferLimit时,即上次的32768已经传完了,此时将bufferOffset重新设置为0。
最后一次传输,bufferLimit的大小为剩下的所有数据,并不一定填满缓冲区最大值。
2013-10-08 14:56:12.855FtpUpLoad[3278:c07] NSStreamEventOpenCompleted
2013-10-08 14:56:12.856FtpUpLoad[3278:c07] NSStreamEventHasSpaceAvailable
2013-10-08 14:56:12.856FtpUpLoad[3278:c07] bufferOffset is 0
2013-10-08 14:56:12.856FtpUpLoad[3278:c07] bufferLimit is 0
2013-10-08 14:56:12.856FtpUpLoad[3278:c07] NSStreamEventHasSpaceAvailable
2013-10-08 14:56:12.857FtpUpLoad[3278:c07] bufferOffset is 32768
2013-10-08 14:56:12.857FtpUpLoad[3278:c07] bufferLimit is 32768
。。。
2013-10-08 14:56:12.859FtpUpLoad[3278:c07] NSStreamEventHasSpaceAvailable
2013-10-08 14:56:12.859FtpUpLoad[3278:c07] bufferOffset is 19308
2013-10-08 14:56:12.859FtpUpLoad[3278:c07] bufferLimit is 32768
。。。
2013-10-08 14:56:12.883FtpUpLoad[3278:c07] bufferOffset is 2308
2013-10-08 14:56:12.883FtpUpLoad[3278:c07] bufferLimit is 2308
处理上传结果:
view
sourceprint?
在此方法中关闭链接、循环、设置代理为空。
由于网上关于在iOS端ftp上传的资料很少,而且刚好笔者也需要用到ftp,因此,便促成了此文的诞生。
一、 简介
在iOS端的ftp上传使用的是CFNetWork框架,它处于核心服务框架中,提供了一个抽象化的网络协议库。使用CFNetWork框架中的CFFTPStream类提供的API能够做很多ftp操作,例如上传文件、下载文件、下载目录列表,创建远程目录等等。但是在使用上也存在一些的限制,CFFTPStream 提供的ftp操作比较基础,它并不是完全的FTP客户端API,不支持所有的FTP操作。
例如:
FTPS(FTP over TLS)
删除文件(没法像Android里面的comment-net包那样可以直接删除服务器文件)
重命名文件
自定义FTP命令
另外,CFFTPStream的目录解析也局限于简单的目录,只能够解析一般的基于UNIX FTP服务的目录。
二、 配置
要使用CFFTPStream的功能,需要导入CFNetWork 框架,方便的是不用去下载第三方包,直接在Xcode里面添加就好了。
1)添加CFNetWork framework
第一步:第二步:
2)新建工程,取名为FtpUpLoad
建立SingleView的项目,配置好nib文件。包括以下控件:文件路径输入框
服务器路径输入框
账号输入框
密码输入框
传输状态显示
上传按钮
三、 核心代码
1)YGViewController.h(根控制器)
viewsourceprint?
01.
#import <UIKit/UIKit.h>
02.
enum
{
03.
kSendBufferSize = 32768
//上传的缓冲区大小,可以设置
04.
};
05.
@interface YGViewController : UIViewController <UITextFieldDelegate,NSStreamDelegate>{
06.
uint8_t _buffer[kSendBufferSize];
07.
}
08.
@property (retain, nonatomic) IBOutlet UITextField *fileInput;
//文件路径
09.
@property (retain, nonatomic) IBOutlet UITextField *serverInput;
//服务器路径
10.
@property (retain, nonatomic) IBOutlet UITextField *accountInput;
//账号
11.
@property (retain, nonatomic) IBOutlet UITextField *passwordInput;
//密码
12.
@property (retain, nonatomic) IBOutlet UILabel *status;
//状态栏
13.
- (IBAction)sendAction:(id)sender;
//点击上传事件
14.
- (IBAction)textFieldDoneEditing:(id)sender;
//Did End On Exit 事件
15.
@end
使用CFFTPStream进行上传,首先要遵循NSStreamDelegate协议。并实现协议中的委托方法。
下面为NSStreamDelegate协议的内容。
@protocol NSStreamDelegate <NSObject>
@optional
- (void)stream:(NSStream *)aStreamhandleEvent:(NSStreamEvent)eventCode;
@end
2)YGViewController.m
定义相关内部变量与存取方法:view
sourceprint?
01.
@interface YGViewController ()
02.
//内部变量
03.
@property (nonatomic, readonly)
BOOL
isSending;
04.
@property (nonatomic, retain) NSOutputStream *networkStream;
05.
@property (nonatomic, retain) NSInputStream *fileStream;
06.
@property (nonatomic, readonly)uint8_t *buffer;
07.
@property (nonatomic, assign)
size_t
bufferOffset;
08.
@property (nonatomic, assign)
size_t
bufferLimit;
09.
@end
10.
11.
//存取方法
12.
@implementation YGViewController
13.
@synthesize fileInput = _fileInput;
14.
@synthesize serverInput = _serverInput;
15.
@synthesize status = _status;
16.
@synthesize accountInput = _accountInput;
17.
@synthesize pass<a href=
"http://www.it165.net/edu/ebg/"
target=
"_blank"
class
=
"keylink"
>word</a>Input = _pass<a href=
"http://www.it165.net/edu/ebg/"
target=
"_blank"
class
=
"keylink"
>word</a>Input;
18.
19.
//buffer getter
20.
- (uint8_t *)buffer
21.
{
22.
return
self->_buffer;
23.
}
size_t与uint8_t为c扩展的类型变量,在用于跨语言的时候比较方便,uint8_t为一个字节大小的无符号int类型,size_t在32位与64位机器上代表不同的大小,分别为4个字节与8个字节。
因为buffer被声明为一个数组,所以必须要使用自定义的getter方法,synthesised方法是不会被编译的。
点击上传按钮,并生成networkStream流
view
sourceprint?
01.
- (IBAction)sendAction:(id)sender {
02.
03.
NSURL *url;
//ftp服务器地址
04.
NSString *filePath;
//图片地址
05.
NSString *account;
//账号
06.
NSString *password;
//密码
07.
CFWriteStreamRef ftpStream;
08.
09.
//获得输入
10.
url = [NSURL URLWithString:_serverInput.text];
11.
filePath = _fileInput.text;
12.
account = _accountInput.text;
13.
password = _passwordInput.text;
14.
15.
//添加后缀(文件名称)
16.
url = [NSMakeCollectable(CFURLCreateCopyAppendingPathComponent(NULL, (CFURLRef) url, (CFStringRef)
17.
[filePath lastPathComponent],
false
)) autorelease];
18.
19.
//读取文件,转化为输入流
20.
self.fileStream = [NSInputStream inputStreamWithFileAtPath:filePath];
21.
[self.fileStream open];
22.
23.
//为url开启CFFTPStream输出流
24.
ftpStream = CFWriteStreamCreateWithFTPURL(NULL, (CFURLRef) url);
25.
self.networkStream = (NSOutputStream *) ftpStream;
26.
27.
//设置ftp账号密码
28.
[self.networkStream setProperty:account forKey:(id)kCFStreamPropertyFTPUserName];
29.
[self.networkStream setProperty:password forKey:(id)kCFStreamPropertyFTPPassword];
30.
31.
//设置networkStream流的代理,任何关于networkStream的事件发生都会调用代理方法
32.
self.networkStream.delegate = self;
33.
34.
//设置runloop
35.
[self.networkStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
36.
[self.networkStream open];
37.
38.
//完成释放链接
39.
CFRelease(ftpStream);
40.
}
实现代理方法:
view
sourceprint?
01.
- (
void
)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode
02.
{
03.
//aStream 即为设置为代理的networkStream
04.
switch
(eventCode) {
05.
case
NSStreamEventOpenCompleted: {
06.
NSLog(@
"NSStreamEventOpenCompleted"
);
07.
}
break
;
08.
case
NSStreamEventHasBytesAvailable: {
09.
NSLog(@
"NSStreamEventHasBytesAvailable"
);
10.
assert
(NO);
// 在上传的时候不会调用
11.
}
break
;
12.
case
NSStreamEventHasSpaceAvailable: {
13.
NSLog(@
"NSStreamEventHasSpaceAvailable"
);
14.
NSLog(@
"bufferOffset is %zd"
,self.bufferOffset);
15.
NSLog(@
"bufferLimit is %zu"
,self.bufferLimit);
16.
if
(self.bufferOffset == self.bufferLimit) {
17.
NSInteger bytesRead;
18.
bytesRead = [self.fileStream read:self.buffer maxLength:kSendBufferSize];
19.
20.
if
(bytesRead == -1) {
21.
//读取文件错误
22.
[self _stopSendWithStatus:@
"读取文件错误"
];
23.
}
else
if
(bytesRead == 0) {
24.
//文件读取完成 上传完成
25.
[self _stopSendWithStatus:nil];
26.
}
else
{
27.
self.bufferOffset = 0;
28.
self.bufferLimit = bytesRead;
29.
}
30.
}
31.
32.
if
(self.bufferOffset != self.bufferLimit) {
33.
//写入数据
34.
NSInteger bytesWritten;
//bytesWritten为成功写入的数据
35.
bytesWritten = [self.networkStream write:&self.buffer[self.bufferOffset]
36.
maxLength:self.bufferLimit - self.bufferOffset];
37.
assert
(bytesWritten != 0);
38.
if
(bytesWritten == -1) {
39.
[self _stopSendWithStatus:@
"网络写入错误"
];
40.
}
else
{
41.
self.bufferOffset += bytesWritten;
42.
}
43.
}
44.
}
break
;
45.
case
NSStreamEventErrorOccurred: {
46.
[self _stopSendWithStatus:@
"Stream打开错误"
];
47.
assert
(NO);
48.
}
break
;
49.
case
NSStreamEventEndEncountered: {
50.
// 忽略
51.
}
break
;
52.
default
: {
53.
assert
(NO);
54.
}
break
;
55.
}
56.
}
bytesWritten为实际写入的数据量,虽然缓冲区大小是固定的值,但每次写入并不一定是充满缓冲区,可以看到NSLog的输出值。(上传图片大小为428492字节,约430k)。
首先是NSStreamEventOpenCompleted打开事件完成的回调,然后不断发送NSStreamEventHasSpaceAvailable消息完成整个上传过程。
可以看到在传输的时候,bufferOffset并不是每次都是32768,所以self.bufferOffset!= self.bufferLimit是一个续传上次未完数据的过程,而当self.bufferOffset = self.bufferLimit时,即上次的32768已经传完了,此时将bufferOffset重新设置为0。
最后一次传输,bufferLimit的大小为剩下的所有数据,并不一定填满缓冲区最大值。
2013-10-08 14:56:12.855FtpUpLoad[3278:c07] NSStreamEventOpenCompleted
2013-10-08 14:56:12.856FtpUpLoad[3278:c07] NSStreamEventHasSpaceAvailable
2013-10-08 14:56:12.856FtpUpLoad[3278:c07] bufferOffset is 0
2013-10-08 14:56:12.856FtpUpLoad[3278:c07] bufferLimit is 0
2013-10-08 14:56:12.856FtpUpLoad[3278:c07] NSStreamEventHasSpaceAvailable
2013-10-08 14:56:12.857FtpUpLoad[3278:c07] bufferOffset is 32768
2013-10-08 14:56:12.857FtpUpLoad[3278:c07] bufferLimit is 32768
。。。
2013-10-08 14:56:12.859FtpUpLoad[3278:c07] NSStreamEventHasSpaceAvailable
2013-10-08 14:56:12.859FtpUpLoad[3278:c07] bufferOffset is 19308
2013-10-08 14:56:12.859FtpUpLoad[3278:c07] bufferLimit is 32768
。。。
2013-10-08 14:56:12.883FtpUpLoad[3278:c07] bufferOffset is 2308
2013-10-08 14:56:12.883FtpUpLoad[3278:c07] bufferLimit is 2308
处理上传结果:
view
sourceprint?
01.
- (
void
)_stopSendWithStatus:(NSString *)statusString
02.
{
03.
if
(self.networkStream != nil) {
04.
[self.networkStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
05.
self.networkStream.delegate = nil;
06.
[self.networkStream close];
07.
self.networkStream = nil;
08.
}
09.
if
(self.fileStream != nil) {
10.
[self.fileStream close];
11.
self.fileStream = nil;
12.
}
13.
[self _sendDidStopWithStatus:statusString];
14.
}
在此方法中关闭链接、循环、设置代理为空。
相关文章推荐
- IOS 入门开发之使用Xcode4制作静态库详解(五)
- 【iOS开发必收藏】详解iOS应用程序内使用IAP/StoreKit付费、沙盒(SandBox)测试、创建测试账号流程!
- 【iOS开发必收藏】详解iOS应用程序内使用IAP/StoreKit付费、沙盒(SandBox)测试、创建测试账号流程
- Objective-C ,ios,iphone开发基础:UIAlertView使用详解
- 开发笔记:FTP命令使用详解
- IOS 入门开发之使用Xcode4制作静态库详解(五)
- 【iOS开发必收藏】详解iOS应用程序内使用IAP/StoreKit付费、沙盒(SandBox)测试、创建测试账号流程!【2012-12-11日更新获取"产品付费数量等于0的问题"】
- Objective-C ,ios,iphone开发基础:picker控件详解与使用,(实现省市的二级联动)
- 【iOS开发】---- UITabBarController的使用详解
- Objective-C ,ios,iphone开发基础:UIAlertView使用详解
- 【iOS开发必收藏】详解iOS应用程序内使用IAP/StoreKit付费、沙盒(SandBox)测试、创建测试账号流程!
- 【iOS开发必收藏】详解iOS应用程序内使用IAP/StoreKit付费、沙盒(SandBox)测试、创建测试账号流程!【2012-6-25日更新iap恢复详解】
- IOS开发NSBundle对象使用详解
- 【iOS开发必收藏】详解iOS应用程序内使用IAP/StoreKit付费、沙盒(SandBox)测试、创建测试账号流程!【2012-12-11日更新获取”产品付费数量等于0的问题”】
- 【iOS开发必收藏】详解iOS应用程序内使用IAP/StoreKit付费、沙盒(SandBox)测试、创建测试账号流程!
- 【iOS开发必收藏】详解iOS应用程序内使用IAP/StoreKit付费、沙盒(SandBox)测试、创建测试账号流程!2012-6-25日更新iap恢复
- 【iOS开发必收藏】详解iOS应用程序内使用IAP/StoreKit付费、沙盒(SandBox)测试、创建测试账号流程!2012-6-25日更新iap恢复
- Objective-C ,ios,iphone开发基础:picker控件详解与使用,(实现省市的二级联动)
- Objective-C ,ios,iphone开发基础:picker控件详解与使用,(实现省市的二级联动)
- 【iOS开发必收藏】详解iOS应用程序内使用IAP/StoreKit付费、沙盒(SandBox)测试、创建测试账号流程!【2012-12-11日更新获取"产品付费数量等于0的问题"】