base64 NSDATAチャンクを使用してファイルをエンコードします
-
27-09-2019 - |
質問
更新4
Gregの提案によると、100Kチャンクを使用して、37K画像からBase64エンコードまでの出力を示す画像/テキストのペアを作成しました。ファイルはわずか37kであるため、ループが一度だけ反復したと言っても安全ですので、何も追加されませんでした。もう1つのペアは、10Kチャンクを使用して、同じ37K画像からBase64への出力をbase64に示します。ファイルは37Kであるため、ループは4回反復し、データは間違いなく追加されました。
2つのファイルでdiffを実行すると、10kbチャンクファイルに214行目から始まり、640行目で終了する大きな違いがあることが示されています。
- 小さな画像(37K)-100Kチャンク - 画像出力
- 小さな画像(37K)-100Kチャンク-Base64テキスト出力
- 小さな画像(37K)-10Kチャンク - 画像出力
- 小さな画像(37K)-10Kチャンク-Base64テキスト出力
更新3
これが私のコードが現在あるところです。少しクリーンアップしましたが、それでも同じ効果を生み出しています。
// Read data in chunks from the original file [originalFile seekToEndOfFile]; NSUInteger fileLength = [originalFile offsetInFile]; [originalFile seekToFileOffset:0]; NSUInteger chunkSize = 100 * 1024; NSUInteger offset = 0; while(offset < fileLength) { NSData *chunk = [originalFile readDataOfLength:chunkSize]; offset += chunkSize; // Convert the chunk to a base64 encoded string and back into NSData NSString *base64EncodedChunkString = [chunk base64EncodedString]; NSData *base64EncodedChunk = [base64EncodedChunkString dataUsingEncoding:NSASCIIStringEncoding]; // Write the encoded chunk to our output file [encodedFile writeData:base64EncodedChunk]; // Cleanup base64EncodedChunkString = nil; base64EncodedChunk = nil; // Update progress bar [self updateProgress:[NSNumber numberWithInt:offset] total:[NSNumber numberWithInt:fileLength]]; }
更新2
したがって、100 kbを超えるファイルがスクランブルされますが、100 kb未満のファイルは問題ありません。私のバッファ/数学/などに何かが外れていることは明らかですが、私はこれで迷子になりました。一日と呼ぶ時かもしれませんが、私はこれを解決して眠りに行きたいです。
これが例です:
更新1
いくつかのテストを行った後、私は同じコードが小さな画像で正常に機能するが、どんなサイズの大きな画像やビデオでも機能しないことを発見しました。間違いなくバッファの問題のように見えますよね?
ねえ、ベース64を試して、1つの小さなチャンクをループして行うことにより、大きなファイルをエンコードしようとします。すべてが機能しているようですが、ファイルは常に破損しています。私がここでどこで間違っているのかを誰かが指摘できるかどうか興味がありました:
NSFileHandle *originalFile, *encodedFile; self.localEncodedURL = [NSString stringWithFormat:@"%@-base64.xml", self.localURL]; // Open the original file for reading originalFile = [NSFileHandle fileHandleForReadingAtPath:self.localURL]; if (originalFile == nil) { [self performSelectorOnMainThread:@selector(updateStatus:) withObject:@"Encoding failed." waitUntilDone:NO]; return; } encodedFile = [NSFileHandle fileHandleForWritingAtPath:self.localEncodedURL]; if (encodedFile == nil) { [self performSelectorOnMainThread:@selector(updateStatus:) withObject:@"Encoding failed." waitUntilDone:NO]; return; } // Read data in chunks from the original file [originalFile seekToEndOfFile]; NSUInteger length = [originalFile offsetInFile]; [originalFile seekToFileOffset:0]; NSUInteger chunkSize = 100 * 1024; NSUInteger offset = 0; do { NSUInteger thisChunkSize = length - offset > chunkSize ? chunkSize : length - offset; NSData *chunk = [originalFile readDataOfLength:thisChunkSize]; offset += [chunk length]; NSString *base64EncodedChunkString = [chunk base64EncodedString]; NSData *base64EncodedChunk = [base64EncodedChunkString dataUsingEncoding:NSASCIIStringEncoding]; [encodedFile writeData:base64EncodedChunk]; base64EncodedChunkString = nil; base64EncodedChunk = nil; } while (offset < length);
解決
パディングについての彼の元のポイントが根本的な問題だったので、Greginyegに信用を与えることができたらいいのにと思います。 Base64を使用すると、各チャンクは3の倍数でなければなりません。そのため、これにより問題が解決されました。
chunkSize = 3600
私がそれを手に入れたら、腐敗はなくなりました。しかし、その後、私はメモリリークの問題に出くわしたので、この投稿から取ったAutorelease Pool Apprachを追加しました。 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];
他のヒント
base64データを画像にどのように変換していますか?いくつかの実装は、受け入れる最大線の長さを制限します。非常に多くのキャラクターごとにラインブレイクを挿入してみてください。