9

説明: アップロードするファイルを表す HTTP POST パラメータで、base 64 でエンコードされた文字列を送信する必要があります。

現在の方法: ASIFormDataRequest を使用して、ファイルを次のように base64 文字列にエンコードしています。

NSData *data = [[NSFileManager defaultManager] contentsAtPath:@"my path here"];
NSString *base64Data = [data encodeBase64ForData];

ただし、大きなファイルをアップロードすると、アプリがメモリ不足になり、恐ろしい死に至ります。

提案された解決策:ディスクからファイルを読み取り、それをbase64文字列にチャンクで変換し、変換されたbase64文字列をHTTPリクエストのパラメータに添付する方法を教えてください。

ファイル全体をメモリに読み込まないようにする効果的な方法。

どんな入力でも大歓迎です!

完全な私のhttpリクエストコード:

NSURL *url = [NSURL URLWithString:requestProgressURLString];
NSData *data = [[NSFileManager defaultManager] contentsAtPath:evidence.uri];
NSString *base64Data = [data encodeBase64ForData];
NSURL *fileURL = [[NSURL alloc] initWithString:evidence.uri];

request = [ASIFormDataRequest requestWithURL:url];

request.shouldAttemptPersistentConnection = false;
[request setShowAccurateProgress:YES];

[request setPostValue:accessToken forKey:@"AccessToken"];
[request setPostValue:[[fileURL path] lastPathComponent] forKey:@"Filename"];
[request setPostValue:evidence.name forKey:@"Title"];
[request setPostValue:base64Data forKey:@"FileData"]; //wants string value (base64 encoded)          
[request addRequestHeader:@"Content-Type" value:@"application/x-www-form-urlencoded"];
[request setTimeOutSeconds:60];
[request setUploadProgressDelegate:self];
[request setDownloadProgressDelegate:self];
[request setDelegate:self];
[request setDidFinishSelector:@selector(requestDone:)];
[request setDidFailSelector:@selector(requestWentWrong:)];
4

2 に答える 2

3

この問題を 2 つに分割します。

1. 大きなファイルをチャンク単位で Base64 にエンコードします
2. そのファイルをアップロードします


1. 大きなファイルをチャンク単位で Base64 にエンコードします。

NSDataインスタンスを Base64 にエンコードするコードを見つけます。次に、1 つNSInputStreamを使用してデータをチャンクで読み取り、エンコードしNSOutputStreamて、一時ファイルへの書き込みに使用します。この質問/回答を見つけました:ファイルをチャンクでbase64エンコードすることは可能ですか? チャンク サイズを 3 の倍数にすると、機能するはずです。これをバックグラウンド スレッドに移動することもできます。

重要:これにより、元のファイルよりも大きな一時ファイルが作成されます。その瞬間に十分なスペースがデバイスにある必要があります。アップロードを開始したら、削除できます。


2.そのファイルをアップロードします

あなたはすでにこれを行っているようです。例とまったく同じように使用できASIFormDataRequestます。また、ファイルのプライベート コピーが作成されるため (マルチパート POST ファイルが作成されます)、コピーを削除できます。

これらの手順を並列化して最適化する場合は、マルチパート ファイルをアップロードする別の方法を見つける必要があります。

于 2012-12-02T12:42:13.167 に答える
0

1回のヒットでファイルを読み取る代わりに、チャンクで読み取る必要があります

入力ストリームを使用してこれを行うことができます Appleはここにいくつかの情報を持っています

基本的に、例のようにファイルを指す NSInputStream を作成する必要があります

- (void)setUpStreamForFile:(NSString *)path {
    // iStream is NSInputStream instance variable
    iStream = [[NSInputStream alloc] initWithFileAtPath:path];
    [iStream setDelegate:self];
    [iStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
        forMode:NSDefaultRunLoopMode];
    [iStream open];
}

次に、NSStreamDelegate メソッド (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode を使用して、チャンクでデータを読み取ることができます。

繰り返しますが、Appleはこれの例を挙げています

- (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode {

    switch(eventCode) {
        case NSStreamEventHasBytesAvailable:
        {
            if(!_data) {
                _data = [[NSMutableData data] retain];
            }
            uint8_t buf[1024];
            unsigned int len = 0;
            len = [(NSInputStream *)stream read:buf maxLength:1024];
            if(len) {
                [_data appendBytes:(const void *)buf length:len];
                // bytesRead is an instance variable of type NSNumber.
                [bytesRead setIntValue:[bytesRead intValue]+len];

                // DO SOMETHING with DATA HERE

            } else {
                NSLog(@"no buffer!");
            }
            break;
        }

選択するチャンクのサイズは、明らかに HTTP ヘッダーのオーバーヘッドがあるため、メモリ パフォーマンスとネットワーク パフォーマンスのバランスをとる必要があります。

NSData を直接送信することでさらに最適化することもできます (したがって、base64 のオーバーヘッドを節約できます) この投稿を確認してください

iPhoneプログラミングでのHTTPサーバーへのファイルアップロード

于 2012-11-13T12:24:46.647 に答える