iOS 中的大型 Base64 上传
Posted
技术标签:
【中文标题】iOS 中的大型 Base64 上传【英文标题】:Large Base64 uploads in iOS 【发布时间】:2012-10-23 06:49:43 【问题描述】:说明: 我需要在代表要上传的文件的 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:)];
【问题讨论】:
我从未见过分块 Base64 转换器,但采用开源实现并将其转换为分块应该不会太难。 Base64 并不那么复杂。 您也可以控制 Web 服务器吗?意思是你也可以在那里修改代码吗?当你说大文件时?多大? 100 MB? 500 MB? 1 GB? 目前上传限制为 100MB,尽管未来可能会增加。理想情况下,无论上传的大小如何,该解决方案都应该有效,因为我确信此代码将用于更多项目。我现在正在看流... 你的服务器支持多部分上传吗? 如果你也控制服务器,base64会非常低效,最好避免。 【参考方案1】:我会把这个问题一分为二:
1.将大文件分块编码为 Base642.上传该文件
1.将大文件分块编码为 Base64:
找到一些将NSData
实例编码为 Base64 的代码。然后使用NSInputStream
读取块中的数据,对其进行编码并使用NSOutputStream
写入临时文件。我找到了这个问题/答案:Is it possible to base64-encode a file in chunks? 将块大小设为 3 的倍数,它应该可以工作。您可能希望将其移至后台线程。
重要提示:这将创建一个比原始文件大的临时文件。在那一刻,设备上必须有足够的空间。开始上传后,您可以将其删除。
2。上传该文件
看来你已经完成了。您可以像在您的示例中一样使用 ASIFormDataRequest
。由于它会生成文件的私有副本(它会构建多部分 POST 文件),因此您可以删除您的副本。
如果您想通过并行化这些步骤来优化它,您将需要找到另一种上传多部分文件的方法。
【讨论】:
【参考方案2】:您需要分块读取文件,而不是一次性读取文件
您可以使用输入流来执行此操作 苹果有一些关于here的信息
基本上,您需要创建一个指向您的文件的 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 开销)检查这篇文章
File Upload to HTTP server in iphone programming
【讨论】:
以上是关于iOS 中的大型 Base64 上传的主要内容,如果未能解决你的问题,请参考以下文章
使用 Alamofire 将图像作为 base64String 上传