问题
I use the following codes to upload video to server, which requires me to convert the video from video format to NSData
. However, when the video is large (e.g. 10 minute video), the App crashes due to memory pressure. How can I resolve this?
- (void)uploadVideo {
NSDictionary *params = nil;
NSString *NSURLSessionIdentifier = [NSString stringWithFormat:@"%@%@",@"my.bundle.identifier.",[self getTimeString]];
NSURLSessionConfiguration *sessionConfig;
// SessionConfiguration With iOS Version
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0) {
sessionConfig = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:NSURLSessionIdentifier];
} else {
sessionConfig = [NSURLSessionConfiguration backgroundSessionConfiguration:NSURLSessionIdentifier];
}
sessionConfig.HTTPMaximumConnectionsPerHost = 1;
NSURLSession *uploadSession = [NSURLSession sessionWithConfiguration:sessionConfig delegate:self delegateQueue:[NSOperationQueue new]];
OMGMultipartFormData *multipartFormData = [OMGMultipartFormData new];
NSString *url = @"SOME_UPLOAD_URL";
// ========= PROBLEMATIC LINE below =========
self.video_data = [NSData dataWithContentsOfURL:self.video_url];
// ========= PROBLEMATIC LINE above =========
[multipartFormData addFile:self.video_data parameterName:@"file" filename:@"file.mp4" contentType:@"video/mp4"];
NSURLRequest *rq = [OMGHTTPURLRQ POST:url:multipartFormData];
id path = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"upload.NSData"];
[rq.HTTPBody writeToFile:path atomically:YES];
[[uploadSession uploadTaskWithRequest:rq fromFile:[NSURL fileURLWithPath:path]] resume];
}
p.s. self.video_url
is a file URL given by UIImagePickerController
which filters only video to choose. I then choose a 10-minute video.
p.s. I got AFNetworking
in same App too, can it help with background transfer?
回答1:
You should be able to this by using a NSMutableURLRequest and utilizing its setHTTPBodyStream setter.
The following are snippets adapted from some code of mine. It handled well for video way over 10 mins. mostly large videos of 60 - 90 minutes.
NSData *movieData = [NSData dataWithContentsOfFile:theMovieSourceString];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setHTTPMethod:@"POST"];
[request setValue:@"video/quicktime" forHTTPHeaderField:@"Content-Type"];
[request setValue:[NSString stringWithFormat:@"attachment; filename=\"%@\"",yourMovieSourceString] forHTTPHeaderField:@"Content-Disposition"];
[request setValue:[NSString stringWithFormat:@"%ld",(unsigned long)[movieData length]] forHTTPHeaderField:@"Content-Length"];
[request setHTTPBodyStream:[NSInputStream inputStreamWithFileAtPath:yourMovieSourceString]];
You can now use this request with your NSURLConnection
NSURLConnection *connection =[[NSURLConnection alloc] initWithRequest:request delegate:self];
回答2:
Issue is obvious - 10 min video file is too big to be stored in a memory what
self.video_data = [NSData dataWithContentsOfURL:self.video_url];
does.
Solution is not to store all request body in a memory. The simplest way is to use NSURLRequest's HTTPBodyStream property. You can create NSInputStream yourself but since you already have AFNetworking it is much easier to use it. In my project I do it this way:
// data.fields is a dictionary with params
NSMutableURLRequest *request = [[AFHTTPRequestSerializer serializer]
multipartFormRequestWithMethod:@"POST"
URLString:[url absoluteString]
parameters:nil
constructingBodyWithBlock:^(id<AFMultipartFormData> formData)
{
[data.fields enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
[formData appendPartWithFormData:[obj dataUsingEncoding:NSUTF8StringEncoding] name:key];
}];
[formData appendPartWithFileURL:fileURL name:@"file_0" error:&error2];
}
error:&error];
来源:https://stackoverflow.com/questions/31181322/upload-large-video-via-nsurlsession-causes-memory-pressure-crash