您的位置:首页 > 移动开发 > IOS开发

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里面添加就好了。


1)添加CFNetWork framework

第一步:



第二步:




2)新建工程,取名为FtpUpLoad

建立SingleView的项目,配置好nib文件。包括以下控件:



文件路径输入框

服务器路径输入框

账号输入框

密码输入框

传输状态显示

上传按钮


三、 核心代码


1)YGViewController.h(根控制器)

view
sourceprint?

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.
}


在此方法中关闭链接、循环、设置代理为空。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐