8

更新 4
Greg の提案に従って、100k チャンクを使用して、37k 画像から base64 エンコードへの出力を示す画像/テキストのペアを 1 つ作成しました。ファイルはわずか 37k であるため、ループは 1 回だけ繰り返され、何も追加されなかったと言っても過言ではありません。もう 1 つのペアは、10k チャンクを使用して base64 でエンコードされた、同じ 37k 画像からの出力を示しています。ファイルは 37k であるため、ループは 4 回繰り返され、データは確実に追加されました。

2 つのファイルの diff を実行すると、10kb のチャンク ファイルでは、214 行目から 640 行目までに大きな違いがあることがわかります。

更新 3
これが私のコードの現在の場所です。少しクリーンアップしましたが、それでも同じ効果が得られます:

// 元のファイルからチャンク単位でデータを読み取ります
[originalFile seekToEndOfFile];
NSUInteger fileLength = [originalFile offsetInFile];
[originalFile seekToFileOffset:0];
NSUInteger チャンクサイズ = 100 * 1024;
NSUInteger オフセット = 0;

while(オフセット < ファイル長) {
    NSData *chunk = [originalFile readDataOfLength:chunkSize];
    オフセット += チャンクサイズ;

    // チャンクを base64 でエンコードされた文字列に変換し、NSData に戻します
    NSString *base64EncodedChunkString = [チャンク base64EncodedString];
    NSData *base64EncodedChunk = [base64EncodedChunkString dataUsingEncoding:NSASCIIStringEncoding];

    // エンコードされたチャンクを出力ファイルに書き込みます
    [encodedFile writeData:base64EncodedChunk];

    // 掃除
    base64EncodedChunkString = nil;
    base64EncodedChunk = nil;

    // プログレスバーを更新
    [self updateProgress:[NSNumber numberWithInt:offset] total:[NSNumber numberWithInt:fileLength]];
}

Update 2
つまり、100 KB を超えるファイルはスクランブルされるように見えますが、100 KB 未満のファイルは問題ありません。私のバッファ/数学/その他で何かがずれていることは明らかですが、これについては迷っています。もう終わりかもしれませんが、この問題を解決して眠りたいです。

次に例を示します。

更新 1
いくつかのテストを行った後、同じコードが小さな画像では正常に機能することがわかりましたが、大きな画像や任意のサイズのビデオでは機能しません。間違いなくバッファの問題のようですね。


やあ、大きなファイルを base64 でエンコードしようとして、一度に 1 つの小さなチャンクをループして実行しようとしています。すべてが機能しているように見えますが、ファイルは常に破損してしまいます。ここで私が間違っている可能性がある場所を誰かが指摘できるかどうか、私は興味がありました:

    NSFileHandle *originalFile, *encodedFile;
    self.localEncodedURL = [NSString stringWithFormat:@"%@-base64.xml", self.localURL];

    // 元のファイルを読み取り用に開く
    originalFile = [NSFileHandle fileHandleForReadingAtPath:self.localURL];
    if (originalFile == nil) {
        [self performSelectorOnMainThread:@selector(updateStatus:) withObject:@"エンコードに失敗しました。" waitUntilDone:いいえ];
        戻る;
    }
    encodedFile = [NSFileHandle fileHandleForWritingAtPath:self.localEncodedURL];
    if (encodedFile == nil) {
        [self performSelectorOnMainThread:@selector(updateStatus:) withObject:@"エンコードに失敗しました。" waitUntilDone:いいえ];
        戻る;
    }

    // 元のファイルからチャンク単位でデータを読み取ります
    [originalFile seekToEndOfFile];
    NSUInteger の長さ = [originalFile offsetInFile];
    [originalFile seekToFileOffset:0];
    NSUInteger チャンクサイズ = 100 * 1024;
    NSUInteger オフセット = 0;
    行う {
        NSUInteger thisChunkSize = 長さ - オフセット > チャンクサイズ ? chunkSize : 長さ - オフセット;
        NSData *chunk = [originalFile readDataOfLength:thisChunkSize];
        オフセット += [チャンク長];

        NSString *base64EncodedChunkString = [チャンク base64EncodedString];
        NSData *base64EncodedChunk = [base64EncodedChunkString dataUsingEncoding:NSASCIIStringEncoding];

        [encodedFile writeData:base64EncodedChunk];

        base64EncodedChunkString = nil;
        base64EncodedChunk = nil;

    while (オフセット < 長さ);
4

2 に答える 2

2

GregInYEG の功績を称えたいと思います。パディングに関する彼の最初の指摘が根底にある問題だったからです。base64 では、各チャンクは 3 の倍数でなければなりません。したがって、これで問題は解決しました。

chunkSize = 3600

それができたら、腐敗はなくなりました。しかし、メモリ リークの問題が発生したため、この投稿から取得した自動解放プール アプローチを追加しました。http://www.cocoadev.com/index.pl?ReadAFilePieceByPiece

最終的なコード:

// Read data in chunks from the original file
[originalFile seekToEndOfFile];
NSUInteger fileLength = [originalFile offsetInFile];
[originalFile seekToFileOffset:0];

// For base64, each chunk *MUST* be a multiple of 3
NSUInteger chunkSize = 24000;
NSUInteger offset = 0;
NSAutoreleasePool *chunkPool = [[NSAutoreleasePool alloc] init];

while(offset < fileLength) {
    // Read the next chunk from the input file
    [originalFile seekToFileOffset:offset];
    NSData *chunk = [originalFile readDataOfLength:chunkSize];

    // Update our offset
    offset += chunkSize;

    // Base64 encode the input chunk
    NSData *serializedChunk = [NSPropertyListSerialization dataFromPropertyList:chunk format:NSPropertyListXMLFormat_v1_0 errorDescription:NULL];
    NSString *serializedString =  [[NSString alloc] initWithData:serializedChunk encoding:NSASCIIStringEncoding];
    NSRange r = [serializedString rangeOfString:@"<data>"];
    serializedString = [serializedString substringFromIndex:r.location+7];
    r = [serializedString rangeOfString:@"</data>"];
    serializedString = [serializedString substringToIndex:r.location-1];

    // Write the base64 encoded chunk to our output file
    NSData *base64EncodedChunk = [serializedString dataUsingEncoding:NSASCIIStringEncoding];
    [encodedFile truncateFileAtOffset:[encodedFile seekToEndOfFile]];
    [encodedFile writeData:base64EncodedChunk];

    // Cleanup
    base64EncodedChunk = nil;
    serializedChunk = nil;
    serializedString = nil;
    chunk = nil;

    // Update the progress bar
    [self updateProgress:[NSNumber numberWithInt:offset] total:[NSNumber numberWithInt:fileLength]];

    // Drain and recreate the pool
    [chunkPool release];
    chunkPool = [[NSAutoreleasePool alloc] init];
}
[chunkPool release];
于 2010-10-06T14:17:26.897 に答える
1

どのようにbase64データを画像に変換していますか? 一部の実装では、受け入れる最大行長が制限されています。非常に多くの文字ごとに改行を挿入してみてください。

于 2010-10-05T02:37:34.043 に答える