使用NSData分块对文件进行Base64编码

8
更新4
根据Greg的建议,我创建了一对图像/文本,显示了从37k图像到base64编码的输出,使用100k块。由于文件只有37k,可以肯定地说循环只迭代了一次,因此没有添加任何内容。另一对显示了相同的37k图像到base64编码的输出,使用10k块。由于文件是37k,循环迭代了四次,数据肯定被添加了。

对这两个文件进行差异比较,可以发现在10kb块文件中,从第214行开始到第640行结束有很大的区别。

更新3
这是我的代码所在的位置。清理了一下,但仍然产生相同的效果:

// 从原始文件中分块读取数据
[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;
// 将块转换为base64编码的字符串,然后再转换回NSData NSString *base64EncodedChunkString = [chunk base64EncodedString]; NSData *base64EncodedChunk = [base64EncodedChunkString dataUsingEncoding:NSASCIIStringEncoding];
// 将编码后的块写入输出文件 [encodedFile writeData:base64EncodedChunk];
// 清理 base64EncodedChunkString = nil; base64EncodedChunk = nil;
// 更新进度条 [self updateProgress:[NSNumber numberWithInt:offset] total:[NSNumber numberWithInt:fileLength]]; }
更新2
所以看起来大于100 KB的文件会被破坏,但是小于100 KB的文件则没有问题。很明显,我的缓冲区/数学等有些问题,但是我在这个问题上迷失了方向。也许该结束一天了,但我想解决这个问题后再睡觉。

这里是一个例子:

更新1
经过测试,我发现同一段代码对于小图片没有问题,但是对于任何大小的大图片或视频都无法正常工作。显然这是一个缓冲问题,对吧?


嗨,我试图通过循环逐个小块进行base64编码来编码大文件,一切似乎都很顺利,但文件总是损坏。我想知道有谁能指出我哪里做错了:

    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:NO]; return; } encodedFile = [NSFileHandle fileHandleForWritingAtPath:self.localEncodedURL]; if (encodedFile == nil) { [self performSelectorOnMainThread:@selector(updateStatus:) withObject:@"编码失败。" waitUntilDone:NO]; return; }
// 从原始文件中以块的形式读取数据 [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);

输出以何种方式被损坏? - Greg
对于图像(JPEG)和视频(Quicktime),文件无法读取。 - frsh
你尝试过将你分块的base64输出与正确的base64输出进行比较吗?在某些情况下,Base64会填充输出,因此可能会有一些块被填充。 - Greg
你能试试这个吗:将块大小降低到足够小,以至于会在较小的图像中导致损坏,生成损坏的输出,然后将其提高到足以产生良好文件的高度。然后,对比两个Base64输出文件,并告诉我们差异是什么。这可能有助于其他人了解情况。 - Greg
谢谢。已更新帖子以反映您的建议。 - frsh
显示剩余7条评论
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];

嘿@frsh...感谢您发布答案,但代码看起来不完整(特别是while循环)...如果可以的话,您能否发布完整的代码...谢谢... - A for Alpha

1
你是如何将 base64 数据转换回图像的?某些实现会限制它们接受的最大行长度。尝试每隔一定字符插入一个换行符。

刚刚尝试了为每个循环添加一个换行符,但没有任何改变。 - frsh
但是你循环中的每个迭代都超过了25,000个字符!尝试每行80个字符之类的东西。 - Alejandro
好的,我刚刚尝试了在每80个字符后添加换行符,但结果仍然相同。不过还是感谢您的建议! - frsh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接