Kam*_*ran 8 http-streaming ios afnetworking-2 nsurlsessiontask
使用AFNetworking 2.0时,我遇到了一些关于流媒体请求的挑战.
我想将大量文件(~50MB)上传到服务器,并且请求必须要流式传输.(否则app会因内存压力而崩溃)
我已尝试过各种方法在AFNetworking 2.0中做到这一点而没有任何成功.这就是我目前正在做的事情:
NSMutableURLRequest *request = [[AFHTTPRequestSerializer serializer] multipartFormRequestWithMethod:@"POST" URLString:baseServiceUrl parameters:nil constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
int imageIndex = 0;
for (NSURL *tempFileUrl in tempFilesUrlList) {
NSString* fileName = [NSString stringWithFormat:@"image%d", imageIndex];
NSError *appendError = nil;
[formData appendPartWithFileURL:tempFileUrl name:fileName error:&appendError];
imageIndex++;
}
} error:&error];
AFHTTPRequestOperation *requestOperation = nil;
requestOperation = [manager HTTPRequestOperationWithRequest:request success:^(AFHTTPRequestOperation *operation, id responseObject) {
DLog(@"Uploading files completed: %@\n %@", operation, responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
DLog(@"Error: %@", error);
}];
[requestOperation setUploadProgressBlock:^(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite) {
double percentDone = (double)totalBytesWritten / (double)totalBytesExpectedToWrite;
DLog(@"progress updated(percentDone) : %f", percentDone);
}];
[requestOperation start];
Run Code Online (Sandbox Code Playgroud)
这工作正常,但它不流.如果请求太大,它准备内存中的请求可能会崩溃.我曾尝试使用原生套接字和流,但Apple表示他们可能因此而拒绝该应用.
我尝试过的另一种方法是:
AFHTTPSessionManager* manager = [AFHTTPSessionManager manager];
NSString *appID = [[NSBundle mainBundle] bundleIdentifier];
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration backgroundSessionConfiguration:appID];
NSURL* serviceUrl = [NSURL URLWithString:baseServiceUrl];
manager = [manager initWithBaseURL:serviceUrl sessionConfiguration:configuration];
NSURLSessionDataTask *uploadFilesTask = [manager POST:baseServiceUrl parameters:nil constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
int imageIndex = 0;
for (NSURL *tempFileUrl in tempFilesUrlList) {
NSString* fileName = [NSString stringWithFormat:@"image%d", imageIndex];
NSError *appendError = nil;
[formData appendPartWithFileURL:tempFileUrl name:fileName error:&appendError];
imageIndex++;
}
} success:^(NSURLSessionDataTask *task, id responseObject) {
DLog(@"Files uploaded successfully: %@\n %@", task, responseObject);
} failure:^(NSURLSessionDataTask *task, NSError *error) {
DLog(@"Error: %@", error);
}];
[progressBar setProgressWithUploadProgressOfTask:uploadFilesTask animated:true];
Run Code Online (Sandbox Code Playgroud)
但这给了我运行时错误说:
由于未捕获的异常'NSGenericException'终止应用程序,原因:'后台会话中的上传任务必须来自文件'***第一次调用堆栈:(0 CoreFoundation 0x02cc75e4 __exceptionPreprocess + 180 1 libobjc.A.dylib
0x02a4a8b6 objc_exception_throw + 44 2 CFNetwork
0x0459eb6c - [__ NSCFBackgroundSessionBridge uploadTaskForRequest:uploadFile:bodyData:completion:] + 994 3
CFNetwork 0x045f2f37 - [__ NSCFURLSession uploadTaskWithStreamedRequest:] + 73
(必须是流媒体,并且能够在后台继续)
我最近解决了这个问题.
我建议使用AFNetworking等网络库.它将使您免于执行之前已解决的单调任务.
为了在后台真正发送请求,您需要将整个请求写入文件,然后使用NSURLSession将其传输到服务器.(这是接受的答案 - 下面的链接)因此,您需要将请求写入磁盘而不将整个文件读入内存.
您可以流式传输文件(不在后台),例如:
NSMutableURLRequest *request = [[AFHTTPRequestSerializer serializer] multipartFormRequestWithMethod:@"POST" URLString:@"http://example.com/upload" parameters:nil constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
[formData appendPartWithFileURL:[NSURL fileURLWithPath:@"file://path/to/image.jpg"] name:@"file" fileName:@"filename.jpg" mimeType:@"image/jpeg" error:nil];
} error:nil];
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
NSProgress *progress = nil;
NSURLSessionUploadTask *uploadTask = [manager uploadTaskWithStreamedRequest:request progress:&progress completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) {
if (error) {
NSLog(@"Error: %@", error);
} else {
NSLog(@"%@ %@", response, responseObject);
}
enter code here
}];
[uploadTask resume];
Run Code Online (Sandbox Code Playgroud)
http://cocoadocs.org/docsets/AFNetworking/2.5.0/
注意:一个简单的解决方案是请求您的应用在后台运行的时间.当applicationDidEnterBackground:叫上你的应用程序代理,你可以打电话[[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:]问了一些时间在后台运行.([[UIApplication sharedApplication] endBackgroundTask:];说你已经完成了.)你问的有什么缺点?您将获得有限的时间."如果你没有调用endBackgroundTask:对于每个任务在时间到期之前,系统会杀死应用程序.如果你在handler参数中提供一个块对象,系统会在时间到期之前调用你的处理程序,以便你有机会结束任务. "
进展
您可以使用子/父关系跟踪多个NSProgress对象的进度.您可以创建一个NSProgress *overallProgress对象,然后[overallProgress becomeCurrentWithPendingUnitCount:<unit size>];在创建新请求之前和之后调用[overallProgress resignCurrent];.您的overallProgress对象将反映所有孩子的进度.
| 归档时间: |
|
| 查看次数: |
1217 次 |
| 最近记录: |