在网上搜索了一些资料下载远程文件夹及其里面的内容,然后结果都只是下载单个文件,并没有提及到下载文件夹中的东西。然后研究了下一个三方的ftp下载文件内容之后,解决了下载远程文件夹中内容的问题。

1.可以将远程文件夹的内容打包成zip的形式,然后通过下载单个文件的形式先下载到沙盒,然后解压沙盒中为zip。下载单个文件可以参照我写的http下载文件。网上也有ftp下载单个文件资料,自己查找。我用的是SSZipArchive这个三方来解压。

2.直接下载,不用压缩。用的三方是GoldRaccoon这个三方

1.)去下载三方并且导入Sources

免费 FTP 上传 ios ios 文件 ftp_免费 FTP 上传 ios

2.)在Build Phases中添加CFNetwork和Foundation包

免费 FTP 上传 ios ios 文件 ftp_解决中文乱码问题_02

3.)目录结构类似这样子的

免费 FTP 上传 ios ios 文件 ftp_解决中文乱码问题_03

4.)我直接在Main.storyboard中添加一个button并关联成事件做点击下载用。

5.)我自己搭建的ftp服务器建立站点,下面是我服务器中的目录

免费 FTP 上传 ios ios 文件 ftp_解决中文乱码问题_04

目录内容:

免费 FTP 上传 ios ios 文件 ftp_解决中文乱码问题_05


5.)代码ViewController中的代码(代码中我已经标注了,就不再说了)


#import "ViewController.h"
#import "GRRequestsManager.h"
#import "GRListingRequest.h"

@interface ViewController ()<GRRequestsManagerDelegate>
@property (nonatomic, strong) GRRequestsManager *requestsManager;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
}

#pragma mark *** Events ***
// 列表
- (IBAction)respondsToListing:(UIButton *)sender {
    //列表localuser下的文件夹目录,这里做的只是列表目录下面有什么
    [self.requestsManager addRequestForListDirectoryAtPath:@"localuser"];
    [self.requestsManager startProcessingRequests];
}

#pragma mark *** GRRequestsManagerDelegate ***
//代理方法,每次执行列表方法都会走这个方法
- (void)requestsManager:(id<GRRequestsManagerProtocol>)requestsManager didCompleteListingRequest:(id<GRRequestProtocol>)request listing:(NSArray *)listing{
    //沙盒目录
    NSString *documentsDirectoryPath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingString:@"/localuser"];
    GRListingRequest *req = (GRListingRequest *)request;
    //远程文件夹列表
    NSLog(@"%@",listing);
    //打印创建的目录
    NSLog(@"%@",documentsDirectoryPath);
    NSFileManager *fileManager = [NSFileManager defaultManager];
    //文件夹列表枚举
    [listing enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        if ([[obj pathExtension] isEqualToString:@""]) {
            //整个地址,在沙盒中创建和远程文件一样的文件夹目录
            NSString *zhenggedizhi = [documentsDirectoryPath stringByAppendingString:[NSString stringWithFormat:@"%@/%@",req.path,obj]];
            //如果沙盒中的这个文件夹不存在就创建
            if (![fileManager fileExistsAtPath:zhenggedizhi]) {
                [fileManager createDirectoryAtPath:zhenggedizhi withIntermediateDirectories:YES attributes:nil error:nil];
            }
            //然后接着递归列举文件夹下的子文件夹下的目录
            [self.requestsManager addRequestForListDirectoryAtPath:[NSString stringWithFormat:@"%@%@",req.path,obj]];
            [self.requestsManager startProcessingRequests];
        }else{
            //如果目录下的不是文件夹,则下载这个文件到沙盒的指定目录下
            [self.requestsManager addRequestForDownloadFileAtRemotePath:[NSString stringWithFormat:@"%@/%@",req.path,obj] toLocalPath:[documentsDirectoryPath stringByAppendingPathComponent:[NSString stringWithFormat:@"%@%@",req.path,obj]]];
            [self.requestsManager startProcessingRequests];
        }
    }];
    
    
}
#pragma mark *** Lazy loading ***
- (GRRequestsManager *)requestsManager{
    if (!_requestsManager) {
        //初始化请求类,需要ftp的地址,用户名密码
        //这里是我自己在服务器上搭建了一个ftp服务器,并建立站点
        _requestsManager = [[GRRequestsManager alloc]initWithHostname:@"ftp://10.185.36.12:81" user:@"zhaoqian" password:@"Zq123"];
        // 设置代理
        _requestsManager.delegate = self;
    }
    return _requestsManager;
}
@end



6.)效果

首先是列表和沙盒地址

免费 FTP 上传 ios ios 文件 ftp_#import_06

comond+shift+g打开沙盒地址看到

免费 FTP 上传 ios ios 文件 ftp_解决中文乱码问题_07

也就是我下载成功了。


注意:暂时不支持中文文件名的文件下载和文件创建。有什么问题可以联系我探讨。希望对大家有帮助!



这两天解决了中文乱码的问题,ftp服务器的编码格式是gbk格式的所以要在一些地方修改

首先是包里面的内容在GRListingRequest.m中找到 case NSStreamEventEndEncountered里面的内容改为:


NSUInteger  offset = 0;
            CFIndex     parsedBytes;
            uint8_t *bytes = (uint8_t *)[self.receivedData bytes];
            NSUInteger totalbytes = [self.receivedData length];
            
            do {
                CFDictionaryRef listingEntity = NULL;
                parsedBytes = CFFTPCreateParsedResourceListing(NULL, &bytes[offset], totalbytes - offset, &listingEntity);
                if (parsedBytes > 0) {
                    if (listingEntity != NULL) {
                        NSDictionary *entryToAdd = [self _entryByReencodingNameInEntry: (__bridge NSDictionary *)listingEntity encoding: CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingGB_18030_2000)];
                        self.filesInfo = [self.filesInfo arrayByAddingObject:entryToAdd];
                    }
                    offset += parsedBytes;
                }
            } while (parsedBytes > 0);
            
            [self.streamInfo streamComplete:self];
            break;



再在下面添加一个方法:


- (NSDictionary*)_entryByReencodingNameInEntry:(NSDictionary *)entry encoding:(NSStringEncoding)newEncoding {
    
    // CFFTPCreateParsedResourceListing always interprets the file name as MacRoman,
    // which is clearly bogus <rdar://problem/7420589>.  This code attempts to fix
    // that by converting the Unicode name back to MacRoman (to get the original bytes;
    // this works because there's a lossless round trip between MacRoman and Unicode)
    // and then reconverting those bytes to Unicode using the encoding provided.
    
    NSDictionary *  result;
    NSString *      name;
    NSData *        nameData;
    NSString *      newName;
    
    newName = nil;
    
    // Try to get the name, convert it back to MacRoman, and then reconvert it
    // with the preferred encoding.
    
    name = [entry objectForKey:(id) kCFFTPResourceName];
    if (name != nil) {
        assert([name isKindOfClass:[NSString class]]);
        
        nameData = [name dataUsingEncoding:NSMacOSRomanStringEncoding];
        if (nameData != nil) {
            newName = [[NSString alloc] initWithData:nameData encoding:newEncoding];
        }
    }
    
    // If the above failed, just return the entry unmodified.  If it succeeded,
    // make a copy of the entry and replace the name with the new name that we
    // calculated.
    
    if (newName == nil) {
        //        assert(NO); // in the debug builds, if this fails, we should investigate why
        result = (NSDictionary *) entry;
    }
    else {
        NSMutableDictionary *   newEntry;
        
        newEntry = [entry mutableCopy];
        assert(newEntry != nil);
        
        [newEntry setObject:newName forKey:(id) kCFFTPResourceName];
        result = newEntry;
    }
    
    return result;
}



其次在GRRequest.m中找到


- (NSString *)encodeString:(NSString *)string;
{
    NSString *urlEncoded = (__bridge_transfer NSString *)CFURLCreateStringByAddingPercentEscapes(
                                                                                                 NULL,
                                                                                                 (__bridge CFStringRef) string,
                                                                                                 NULL,
                                                                                                 (CFStringRef)@"!*'\"();:@&=+$,?%#[]%",
                                                                                                 CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingGB_18030_2000));
    return urlEncoded;
}

将其改成gbk编码的格式

最后在ViewController.m中做出如下修改

添加一个解URLGBK编码的方法


- (NSString *)decodeFromPercentEscapeString: (NSString *) input
{
    NSMutableString *outputStr =  (__bridge NSString *)CFURLCreateStringByReplacingPercentEscapesUsingEncoding(kCFAllocatorDefault,
                                                                                                               (CFStringRef)input,
                                                                                                               CFSTR(""), 
                                                                        kCFStringEncodingGB_18030_2000);
    return outputStr;
}



然后将以前文件中的req.path 改为上面这个方法解码后的字符串

ViewController.m整个代码如下


#import "ViewController.h"
#import "GRRequestsManager.h"
#import "GRListingRequest.h"

@interface ViewController ()<GRRequestsManagerDelegate>
@property (nonatomic, strong) GRRequestsManager *requestsManager;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
}

#pragma mark *** Events ***
// 列表
- (IBAction)respondsToListing:(UIButton *)sender {
    //列表localuser下的文件夹目录,这里做的只是列表目录下面有什么
    [self.requestsManager addRequestForListDirectoryAtPath:@"dirt"];
    [self.requestsManager startProcessingRequests];
}

#pragma mark *** GRRequestsManagerDelegate ***
//代理方法,每次执行列表方法都会走这个方法
- (void)requestsManager:(id<GRRequestsManagerProtocol>)requestsManager didCompleteListingRequest:(id<GRRequestProtocol>)request listing:(NSArray *)listing{
    //沙盒目录
    NSString *documentsDirectoryPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
    NSLog(@"%@",documentsDirectoryPath);
    GRListingRequest *req = (GRListingRequest *)request;
    //远程文件夹列表
    NSLog(@"%@",listing);
    //打印创建的目录
    NSFileManager *fileManager = [NSFileManager defaultManager];
    //文件夹列表枚举
    [listing enumerateObjectsUsingBlock:^(NSString * obj, NSUInteger idx, BOOL * _Nonnull stop) {
        if ([[obj pathExtension] isEqualToString:@""]) {
            //整个地址,在沙盒中创建和远程文件一样的文件夹目录
            NSString *zhenggedizhi = [documentsDirectoryPath stringByAppendingString:[NSString stringWithFormat:@"%@%@",[self decodeFromPercentEscapeString:req.path],obj]];
            //如果沙盒中的这个文件夹不存在就创建
            if (![fileManager fileExistsAtPath:zhenggedizhi]) {
                [fileManager createDirectoryAtPath:zhenggedizhi withIntermediateDirectories:YES attributes:nil error:nil];
            }
            //然后接着递归列举文件夹下的子文件夹下的目录
            [self.requestsManager addRequestForListDirectoryAtPath:[NSString stringWithFormat:@"%@%@",[self decodeFromPercentEscapeString:req.path],obj]];
            [self.requestsManager startProcessingRequests];
        }else{
            if ([obj isEqualToString:@"end.txt"]) {
                NSLog(@"finished.........");
            }
            //如果目录下的不是文件夹,则下载这个文件到沙盒的指定目录下
            if (![fileManager fileExistsAtPath:[documentsDirectoryPath stringByAppendingPathComponent:[NSString stringWithFormat:@"%@%@",[self decodeFromPercentEscapeString:req.path],obj]]]) {
                [self.requestsManager addRequestForDownloadFileAtRemotePath:[NSString stringWithFormat:@"%@%@",[self decodeFromPercentEscapeString:req.path],obj] toLocalPath:[documentsDirectoryPath stringByAppendingPathComponent:[NSString stringWithFormat:@"%@%@",[self decodeFromPercentEscapeString:req.path],obj]]];
                NSLog(@"%@",[NSString stringWithFormat:@"%@%@",[self decodeFromPercentEscapeString:req.path],obj]);
                [self.requestsManager startProcessingRequests];
            }
            
        }
    }];
}

- (NSString *)decodeFromPercentEscapeString: (NSString *) input
{
    NSMutableString *outputStr =  (__bridge NSString *)CFURLCreateStringByReplacingPercentEscapesUsingEncoding(kCFAllocatorDefault,
                                                                                                               (CFStringRef)input,
                                                                                                               CFSTR(""), 
                                                                        kCFStringEncodingGB_18030_2000);
    return outputStr;
}
#pragma mark *** Lazy loading ***
- (GRRequestsManager *)requestsManager{
    if (!_requestsManager) {
        //初始化请求类,需要ftp的地址,用户名密码
        //这里是我自己在服务器上搭建了一个ftp服务器,并建立站点
        _requestsManager = [[GRRequestsManager alloc]initWithHostname:@"ftp://10.185.36.12:81" user:@"zhaoqian" password:@"Zq123"];
        // 设置代理
        _requestsManager.delegate = self;
    }
    return _requestsManager;
}
@end



如果URL路劲不对会出现一个   kCFErrorDomainCFNetwork error 200的错误