2014-02-28 2 views
8

AFNetworking 2.0을 사용하여 스트리밍 요청에 직면하는 몇 가지 문제점이 있습니다.업로드 진행률을 제공 할 수있는 단일 스트리밍 요청으로 AFNetworking을 사용하여 서버에 최대 100 개의 파일을 업로드해야합니다.

많은 파일 (~ 50MB)을 서버에 업로드하려고하며 요청을 스트리밍해야합니다. (그렇지 않으면 메모리 압력으로 인해 응용 프로그램이 충돌합니다.)

나는 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]; 

있지만 스트리밍하지 : 이것은 내가 현재하고있는 중이 것입니다. 요청이 너무 크면 메모리에서 요청을 충돌시킬 수 있습니다. 나는 네이티브 소켓과 스트림을 실험했지만, 애플은 그것 때문에 앱을 비 승인 할 것이라고 말한다.

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]; 

을하지만 내가 말하는 시간 오류를 실행 제공 :

내가 시도 또 다른 방법은 다음 인해 캐치되지 않는 예외 'NSGenericException', 이유에

응용 프로그램을 종료 '업로드 작업 백그라운드 세션에서 파일 ' ***에서 첫 번째 호출 스택을 던져야합니다 : (0 CoreFoundation 0x02cc75e4 __exceptionPreprocess + 180 1 libobjc.A.dylib
0x02a4a8b6 objc_exception_throw + 44 2 CFNetwork에서
0x0459eb6c는 - [__ NSCFBackgroundSessionBridge는 uploadTaskForRequest는 :을 UploadFile : bodyData : 완료 :] + 994 3
CFNetwork에서의 0x045f2f37 - [__ NSCFURLSession uploadTaskWithStreamedRequest :] +

73 ( 스트리밍 이어야하며 계속해서 배경)

+0

iOS는 백그라운드 업로드로 스트리밍을 지원하지 않습니다. 백그라운드에서 지원해야합니까, 아니면 전경에서만 지원해야합니까? 포어 그라운드에서 이론 상으로는 스트리밍 할 수 있어야합니까? 다른 옵션은 서버 측 지원을보고 작은 부분으로 파일을 업로드하는 것입니다. 서버가 다중 부분 업로드 (즉, Amazon S3의 다중 부분 업로드 http://docs.aws.amazon.com/AmazonS3/latest/dev/mpuoverview.html)를 지원합니까? 부품을 사용하면 메모리 오버 헤드를 피하는 작은 부분에 파일을 업로드 할 수 있습니다. –

답변

6

내가 최근에 해결했습니다.

AFNetworking과 같은 네트워킹 라이브러리를 사용하는 것이 좋습니다. 이전에 해결 된 단조로운 작업을하지 않아도됩니다.

진정으로 백그라운드에서 요청을 보내려면 전체 요청을 파일에 기록한 다음 NSURLSession을 사용하여 서버로 스트리밍해야합니다. (그 대답은 아래에 있습니다 - 링크) 그래서, 당신은 메모리에 전체 파일을 읽지 않고 디스크에 요청을 작성해야합니다.

How to upload task in background using afnetworking

당신은 같은 파일을 (하지 배경) 스트리밍 할 수 있습니다 :

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]; 

http://cocoadocs.org/docsets/AFNetworking/2.5.0/

참고 : 간단한 솔루션에서 실행하는 앱 시간을 요청하는 것입니다 배경. 앱 대리인에게 applicationDidEnterBackground:이 호출되면 [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:]으로 전화를 걸어 백그라운드에서 실행 시간을 요청할 수 있습니다. ([[UIApplication sharedApplication] endBackgroundTask:];라고 말하면 끝났습니다.) 무엇이 단점입니까? 제한된 시간이 주어집니다."endBackgroundTask :를 호출하지 않으면 각 태스크가 만료되기 전에 시스템이 앱을 종료합니다. 핸들러 매개 변수에 블록 객체를 제공하면 시스템은 시간이 만료되기 전에 처리기를 호출하여 작업을 종료 할 수 있습니다. "

https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIApplication_Class/#//apple_ref/occ/instm/UIApplication/beginBackgroundTaskWithExpirationHandler

:

진행

당신은 자녀/부모 관계를 사용하여 여러 NSProgress 개체의 진행 상황을 추적 할 수 있습니다. NSProgress *overallProgress 개체를 만들고 새 요청을 만들기 전에 [overallProgress becomeCurrentWithPendingUnitCount:<unit size>];을 호출 한 다음 나중에 [overallProgress resignCurrent];을 호출 할 수 있습니다. overallProgress 객체는 모든 자식의 진행 상황을 반영합니다.

관련 문제