Objective-CのFTPファイル送受信で日本語名ファイルを扱う

# Objective-CのFTPファイル送受信で日本語名ファイルを扱う

iOS端末からFTPサーバーにアクセスし、日本語名のファイルを扱う方法をまとめました。
（今回はWindows上のFTPサーバーのファイルにアクセスするケースを想定しています）

## 準備（FTPライブラリを組み込みます）

FTP送受信のライブラリは[GoldRaccoon](https://github.com/albertodebortoli/GoldRaccoon)を使います。

cocoaPodsを使ってインストールするので、まずcocoaPodsをインストールします。

`sudo gem install cocoapods`

注：rbenvを使っていてエラーになる場合は、systemのrubyを使いましょう。

プロジェクト（例：JapaneaseFTPSample）直下にPodFileを配置します。

```bash:PodFile
pod "GoldRaccoon"
```

GoldRaccoonをインストールします。

`pod install`

`JapaneaseFTPSample.xcworkspace`ができているので、これを起動します。

####注意点

~~`pod install`でインストールしたものが1.0.1の場合、~~
~~iPhone Retina(4-inch 64bit)以外で動かすと例外が発生しました。~~
~~GitHub上にある[最新ソース](https://github.com/albertodebortoli/GoldRaccoon.git)では発生しなくなっているのでSources以下を丸ごと置き換えておきましょう。~~

補足：[アップロード時の不具合](https://github.com/albertodebortoli/GoldRaccoon/issues/4)が修正された1.0.2で最新ソースと一致しています。

## FTP機能の利用

List、Get、Putの機能を使ってみます。

組み込み方法は[GoldRaccoon](https://github.com/albertodebortoli/GoldRaccoon)の手順と、GRDemoViewControllerの実装を参考にしました。

1.初期化
`initWithHostname: user: password:`

2.リクエストの登録

`addRequestForListDirectoryAtPath:`
`addRequestForDownloadFileAtRemotePath: toLocalPath:`
`addRequestForUploadFileAtLocalPath: toRemotePath:`

3.リクエストの実行

`startProcessingRequests`

4.結果受信（デリゲート）

`requestsManager: didScheduleRequest:`
`requestsManager: didCompleteDownloadRequest:`
`requestsManager: didCompleteListingRequest: listing:`
`requestsManager: didCompleteUploadRequest:`
`requestsManager: didFailWritingFileAtPath: forRequest: error:`
`requestsManager: didFailRequest: withError:`

## 日本語ファイル対応のポイント

### ファイルリスト取得時

ファイルリストを取得には`CFFTPCreateParsedResourceListing`を使用しています。
この関数で取得されたファイル名はMacRomanに変換して格納されているため、元に戻す必要があります。

このことは、Apple公式サンプルの[SimpleFTPSampleのListController.m](https://developer.apple.com/library/ios/samplecode/SimpleFTPSample/Listings/ListController_m.html#//apple_ref/doc/uid/DTS40009243-ListController_m-DontLinkElementID_13)の`entryByReencodingNameInEntry:`のところで説明されています。

その後本来のエンコードで取り出します。

```objc
- (void)requestsManager:(id<GRRequestsManagerProtocol>)requestsManager didCompleteListingRequest:(id<GRRequestProtocol>)request listing:(NSArray *)listing
{
    NSLog(@"requestsManager:didCompleteListingRequest:listing: \n%@", listing);
    
    self.fileLists = nil;
    self.fileLists = [[NSMutableArray alloc] init];
    
    // 日本語ファイル名の変換
    NSString *name;
    NSData *nameData;
    NSString *newName;
    for (int i = 0; i<[listing count]; i++) {
        name = listing[i];
        nameData = [name dataUsingEncoding:NSMacOSRomanStringEncoding];
        if (nameData != nil) {
            newName = [[NSString alloc] initWithData:nameData encoding:NSShiftJISStringEncoding];
            [self.fileLists addObject:newName];
        }
    }
    
    self.getButton.enabled = YES;
    [self.fileListView reloadData];
}
```

### ファイルダウンロード時

ファイル名が日本語なのでURLエンコードする必要があります。
pod installでインストールした1.0.1では自前で対応する必要がありましたが、
GitHubから取得したソースでは内部でエンコードされるようになっていました。

しかしUFT8固定のため、クラス拡張を使ってエンコードすることにしました。

```objc:GRRequest+encode.h
#import "GRRequest.h"

@interface GRRequest(Encode)
@end
```

```objc:GRRequest+encode.m
#import "GRRequest+encode.h"
#import <objc/runtime.h>

@implementation GRRequest(Encode)

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

@end
```

リスト取得時を参考にMacRomanに変換します。

```objc
- (IBAction)getFile:(id)sender {
    [self _setupManager];
    
    if (self.ftpFileName == nil) {
        return;
    }

    NSData *nameData = [self.ftpFileName dataUsingEncoding:NSShiftJISStringEncoding];
    if (nameData == nil) {
        return;
    }
    
    NSString *fileName = [[NSString alloc] initWithData:nameData encoding:NSMacOSRomanStringEncoding];

    [self.requestsManager addRequestForDownloadFileAtRemotePath:fileName toLocalPath:[NSTemporaryDirectory() stringByAppendingPathComponent:self.ftpFileName]];
    [self.requestsManager startProcessingRequests];
}
```

### ファイルアップロード時
ダウンロード時と同じように変換します。

```objc
- (IBAction)putFile:(id)sender {
    [self _setupManager];

    NSData *nameData = [self.ftpFileName dataUsingEncoding:NSShiftJISStringEncoding];
    if (nameData == nil) {
        return;
    }
    
    NSString *fileName = [[NSString alloc] initWithData:nameData encoding:NSMacOSRomanStringEncoding];
    
    [self.requestsManager addRequestForUploadFileAtLocalPath:[NSTemporaryDirectory() stringByAppendingPathComponent:self.ftpFileName] toRemotePath:fileName];
    [self.requestsManager startProcessingRequests];
}
```

今回作成したサンプルコードはGitHubにあげておきます。
[JapaneaseFTPSample](https://github.com/t-oginogin/JapaneaseFTPSample)