Phasset + AfNetworking sube varios videos

Actualmente estoy usando el siguiente código para upload videos:

NSURLRequest *urlRequest = [[AFHTTPRequestSerializer serializer] multipartFormRequestWithMethod:@"POST" URLString:[[entity uploadUrl]absoluteString] parameters:entity.params constructingBodyWithBlock:^(id<AFMultipartFormData> formData) { [UploadModel getAssetData:entity.asset resultHandler:^(NSData *filedata) { NSString *mimeType =[FileHelper mimeTypeForFileAtUrl:entity.fileUrl]; // NSError *fileappenderror; [formData appendPartWithFileData:filedata name:@"data" fileName: entity.filename mimeType:mimeType]; }]; } error:&urlRequestError]; 

Método GetAssetData

 +(void)getAssetData: (PHAsset*)mPhasset resultHandler:(void(^)(NSData *imageData))dataResponse{ PHVideoRequestOptions *options = [[PHVideoRequestOptions alloc] init]; options.version = PHVideoRequestOptionsVersionOriginal; [[PHImageManager defaultManager] requestAVAssetForVideo:mPhasset options:options resultHandler:^(AVAsset *asset, AVAudioMix *audioMix, NSDictionary *info) { if ([asset isKindOfClass:[AVURLAsset class]]) { NSURL *localVideoUrl = [(AVURLAsset *)asset URL]; NSData *videoData= [NSData dataWithContentsOfURL:localVideoUrl]; dataResponse(videoData); } }]; } 

El problema con este enfoque es que una aplicación simplemente se queda sin memory cuando se cargan files de video grandes / múltiples. Supongo que se debe a la request del NSDATA (aka filedata ) para la carga de un file (ver el método anterior). Intenté solicitar la ruta del file usando el método appendPartWithFileURL lugar de appendPartWithFileData , funciona en un emulador. y falla en un dispositivo real con un error que no puede leer el file por la ruta especificada. He descrito este problema aquí PHAsset + AFNetworking. No se pueden cargar files al server en un dispositivo real.

=======================================

Actualización: he modificado mi código para probar el enfoque de cargar el file por la ruta local en un nuevo iPhone 6s + de la siguiente manera

  NSURLRequest *urlRequest = [[AFHTTPRequestSerializer serializer] multipartFormRequestWithMethod:@"POST" URLString:[[entity uploadUrl]absoluteString] parameters:entity.params constructingBodyWithBlock:^(id<AFMultipartFormData> formData) { NSString *mimeType =[FileHelper mimeTypeForFileAtUrl:entity.fileUrl]; NSError *fileappenderror; [formData appendPartWithFileURL:entity.fileUrl name:@"data" fileName:entity.filename mimeType:mimeType error:&fileappenderror]; if (fileappenderror) { [Sys MyLog: [NSString stringWithFormat:@"Failed to append: %@", [fileappenderror localizedDescription] ] ]; } } error:&urlRequestError]; 

La testing en iPhone 6s + da una advertencia de logging más clara Se produce como resultado de invocar el método appendPartWithFileURL

  <Warning>: my_log: Failed to append file: The operation couldn't be completed. File URL not reachable. deny(1) file-read-metadata /private/var/mobile/Media/DCIM/100APPLE/IMG_0008.MOV 15:41:25 iPhone-6s kernel[0] <Notice>: Sandbox: My_App(396) deny(1) file-read-metadata /private/var/mobile/Media/DCIM/100APPLE/IMG_0008.MOV 15:41:25 iPhone-6s My_App[396] <Warning>: my_log: Failed to append file: The file “IMG_0008.MOV” couldn't be opened because you don't have permission to view it. 

Aquí está el código utilizado para search la ruta del file local desde PHAsset

 if (mPhasset.mediaType == PHAssetMediaTypeImage) { PHContentEditingInputRequestOptions * options = [[PHContentEditingInputRequestOptions alloc]init]; options.canHandleAdjustmentData = ^BOOL(PHAdjustmentData *adjustmeta){ return YES; }; [mPhasset requestContentEditingInputWithOptions:options completionHandler:^(PHContentEditingInput * _Nullable contentEditingInput, NSDictionary * _Nonnull info) { dataResponse(contentEditingInput.fullSizeImageURL); }]; }else if(mPhasset.mediaType == PHAssetMediaTypeVideo){ PHVideoRequestOptions *options = [[PHVideoRequestOptions alloc] init]; options.version = PHVideoRequestOptionsVersionOriginal; [[PHImageManager defaultManager] requestAVAssetForVideo:mPhasset options:options resultHandler:^(AVAsset *asset, AVAudioMix *audioMix, NSDictionary *info) { if ([asset isKindOfClass:[AVURLAsset class]]) { NSURL *localVideoUrl = [(AVURLAsset *)asset URL]; dataResponse(localVideoUrl); } }]; } 

Entonces, el problema sigue siendo el mismo: los files cargados en el server están vacíos.

La solución propuesta anterior es correcta solo parcialmente (y fue encontrada por mí antes). Dado que el sistema no permite leer files fuera de sandbox, los files no pueden ser accedidos (lectura / escritura) por la ruta del file y simplemente copydos. En la versión iOS 9 y superior, Photos Framework proporciona API (no se puede hacer a través de NSFileManager, sino solo con Photos framework api) para copyr el file en el directory sandbox de la aplicación. Aquí está el código que utilicé después de cavar en documentos y files principales.

En primer lugar, copie un file en el directory de sandbox de la aplicación.

 // Assuming PHAsset has only one resource file. PHAssetResource * resource = [[PHAssetResource assetResourcesForAsset:myPhasset] firstObject]; +(void)writeResourceToTmp: (PHAssetResource*)resource pathCallback: (void(^)(NSURL*localUrl))pathCallback { // Get Asset Resource. Take first resource object. since it's only the one image. NSString *filename = resource.originalFilename; NSString *pathToWrite = [NSTemporaryDirectory() stringByAppendingString:filename]; NSURL *localpath = [NSURL fileURLWithPath:pathToWrite]; PHAssetResourceRequestOptions *options = [PHAssetResourceRequestOptions new]; options.networkAccessAllowed = YES; [[PHAssetResourceManager defaultManager] writeDataForAssetResource:resource toFile:localpath options:options completionHandler:^(NSError * _Nullable error) { if (error) { [Sys MyLog: [NSString stringWithFormat:@"Failed to write a resource: %@",[error localizedDescription]]]; } pathCallback(localpath); }]; } // Write Resource into Tmp 

Cargar tarea por sí misma

  NSURLRequest *urlRequest = [[AFHTTPRequestSerializer serializer] multipartFormRequestWithMethod:@"POST" URLString:[[entity uploadUrl]absoluteString] parameters:entity.params constructingBodyWithBlock:^(id<AFMultipartFormData> formData) { // Assuming PHAsset has only one resource file. PHAssetResource * resource = [[PHAssetResource assetResourcesForAsset:myPhasset] firstObject]; [FileHelper writeResourceToTmp:resource pathCallback:^(NSURL *localUrl) { [formData appendPartWithFileURL: localUrl name:@"data" fileName:entity.filename mimeType:mimeType error:&fileappenderror]; }]; // writeResourceToTmp }// End Url Request AFHTTPRequestOperation * operation = [[AFHTTPRequestOperation alloc ] initWithRequest:urlRequest]; //..... // Further steps are described in the AFNetworking Docs 

Este método de subida tiene un inconveniente importante. Estás atornillado si el dispositivo entra en el "modo de reposo". Por lo tanto, el método de subida recomendado aquí es usar el método. uploadTaskWithRequest:fromFile:progress:completionHandler en AFURLSessionManager.

Para las versiones debajo de iOS 9 .. En caso de imágenes Puede search el NSDATA desde PHAsset como se muestra en el código de mi pregunta … y uploadlo. o escríbalo primero en el almacenamiento de sandbox de su aplicación antes de cargarlo. Este enfoque no es utilizable en el caso de files grandes. Alternativamente, puede utilizar los files de export de Selector de image / video como ALAsset . ALAsset proporciona api que le permite leer el file desde el almacenamiento, pero debe escribirlo en el almacenamiento de sandbox antes de cargarlo.

Crear NSData para cada video puede ser malo, porque los videos (o cualquier otro file) pueden ser mucho más grandes que la RAM del dispositivo, sugiero que se cargue como "file" y no como "datos", si agrega un file, enviará los datos del disco chunk-by-chunk y no intentará leer todo el file a la vez, intente usar

 - (BOOL)appendPartWithFileURL:(NSURL *)fileURL name:(NSString *)name error:(NSError * __autoreleasing *)error 

También eche un vistazo a https://github.com/AFNetworking/AFNetworking/issues/828

En tu caso, utilízalo así.

  NSURLRequest *urlRequest = [[AFHTTPRequestSerializer serializer] multipartFormRequestWithMethod:@"POST" URLString:[[entity uploadUrl]absoluteString] parameters:entity.params constructingBodyWithBlock:^(id<AFMultipartFormData> formData) { [formData appendPartWithFileURL:entity.fileUrl name:entity.filename error:&fileappenderror]; if(fileappenderror) { NSLog(@"%@",fileappenderror); } } error:&urlRequestError]; 

Vea mi respuesta aquí a su otra pregunta. Parece pertinente aquí porque el tema está vinculado.

En esa respuesta describo que, en mi experiencia, Apple no nos da acceso directo a los files fuente de video asociados con un PHAsset. O un ALAsset para ese asunto.

Para acceder a los files de video y uploadlos, primero debe crear una copy usando AVAssetExportSession . O usando la API iOS9 + PHAssetResourceManager .

No debe utilizar ningún método que cargue datos en la memory, ya que rápidamente se ejecutará contra excepciones OOM. Y probablemente no sea una buena idea utilizar el requestAVAssetForVideo(_:options:resultHandler:) como se indicó anteriormente porque a veces obtendrá una Composition AV en lugar de una AVAsset (y no puede get un NSURL de una Composition AV directamente) .

También es probable que no desee utilizar ningún método de carga que aproveche AFHTTPRequestOperation o las API relacionadas porque se basan en la API de NSURLConnection en desuso en lugar de las API de NSURLSession más modernas. NSURLSession le permitirá llevar cargas de video de larga duración en un process en segundo plano, permitiendo a sus usuarios abandonar su aplicación y tener la security de que la carga se completará independientemente.

En mi respuesta original menciono VimeoUpload . Es una biblioteca que maneja las cargas de files de video a Vimeo, pero su núcleo se puede reutilizar para manejar cargas de files de video de background simultáneas a cualquier destino. Publicación completa: Soy uno de los autores de la biblioteca.