iOS FTP上传

来源:互联网 发布:api原油库存最新数据 编辑:程序博客网 时间:2024/06/08 04:34

网络上大多数的文件的上传都是通过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.}

在此方法中关闭链接、循环、设置代理为空。


0 0
原创粉丝点击