Phasset + AfNetworking Télécharger plusieurs videos

Actuellement, j'utilise le code suivant pour mettre en ligne des videos:

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

GetAssetData, méthode

 +(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); } }]; } 

Le problème avec cette approche qu'une application manque simplement de memory chaque fois que de gros / plusieurs files video sont téléchargés. Je suppose que c'est dû à la request de NSDATA (aka filedata ) pour le téléchargement d'un file (voir dans la méthode ci-dessus). J'ai essayé de requestr le path du file en utilisant la méthode appendPartWithFileURL lieu de appendPartWithFileData cela fonctionne sur un émulateur. et échoue sur un périphérique réel avec une erreur qu'il ne peut pas lire le file par le path spécifié. J'ai décrit ce problème ici PHAsset + AFNetworking. Impossible de download des files sur le server sur un périphérique réel

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

Mise à jour: J'ai modifié mon code afin de tester l'approche du téléchargement du file par le path local sur un nouvel iPhone 6s + comme suit

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

Le test sur l'iPhone 6s + donne un avertissement plus clair sur le journal. Il se produit à la suite de l'appel de la méthode 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. 

Voici le code utilisé pour récupérer le path du file local depuis 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); } }]; } 

Le problème rest donc le même: les files téléchargés sur le server sont vides

La solution proposée ci-dessus n'est correcte que partiellement (et elle a été trouvée par moi-même auparavant). Puisque le système ne permet pas de lire les files en dehors du sandbox, les files ne peuvent pas être accédés (lecture / écriture) par le path du file et simplement copiés. Dans la version iOS 9 et au-dessus, Photos Framework fournit des API (cela ne peut pas être fait via NSFileManager, mais uniquement en utilisant l'API Photos Framework) pour copyr le file dans le directory sandbox de votre application. Voici le code que j'ai utilisé après avoir creusé dans docs et files de tête.

Tout d'abord, copyz un file dans le directory sandbox de l'application.

 // 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. NSSsortingng *filename = resource.originalFilename; NSSsortingng *pathToWrite = [NSTemporaryDirectory() ssortingngByAppendingSsortingng: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: [NSSsortingng ssortingngWithFormat:@"Failed to write a resource: %@",[error localizedDescription]]]; } pathCallback(localpath); }]; } // Write Resource into Tmp 

Télécharger la tâche elle-même

  NSURLRequest *urlRequest = [[AFHTTPRequestSerializer serializer] multipartFormRequestWithMethod:@"POST" URLSsortingng:[[entity uploadUrl]absoluteSsortingng] 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 

Cette méthode de téléchargement a un inconvénient important .. vous êtes vissé si l'appareil passe en "mode veille" .. Par conséquent, l'approche de téléchargement recommandée ici est d'utiliser la méthode. uploadTaskWithRequest:fromFile:progress:completionHandler dans AFURLSessionManager.

Pour les versions inférieures à iOS 9 .. En cas d'images Vous pouvez récupérer le NSDATA de PHAsset comme indiqué dans le code de ma question .. et le download. ou écrivez-le d'abord dans le bac à sable de votre application avant de le download. Cette approche n'est pas utilisable dans le cas de files volumineux. Vous pouvez également utiliser les files d'export du sélecteur Image / video comme file ALAsset . ALAsset fournit une API qui vous permet de lire le file à partir du stockage, mais vous devez l'écrire dans le bac à sable avant de le download.

Création de NSData pour chaque video peut être mauvais, parce que les videos (ou d'autres files) peuvent être beaucoup plus grande que la RAM de l'appareil, je suggère de download en tant que «file» et non en tant que «données», enverra datatables du morceau morceau par morceau et n'essaiera pas de lire le file entier à la fois, essayez d'utiliser

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

Jetez également un coup d'oeil à https://github.com/AFNetworking/AFNetworking/issues/828

Dans votre cas, utilisez-le avec ceci

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

Voir ma réponse ici à votre autre question. Cela semble pertinent ici car le sujet est lié.

Dans cette réponse, je décris que dans mon expérience, Apple ne nous donne pas un access direct aux files sources video associés à un PHAsset. Ou un ALAsset d'ailleurs.

Pour accéder aux files video et les download, vous devez d'abord créer une copy en utilisant AVAssetExportSession . Ou en utilisant l'API iOS9 + PHAssetResourceManager .

Vous ne devriez pas utiliser de methods qui chargent des données en memory car vous allez rapidement vous heurter aux exceptions de MOO. Et ce n'est probablement pas une bonne idée d'utiliser la requestAVAssetForVideo(_:options:resultHandler:) comme indiqué ci-dessus car vous aurez parfois un AVComposition par opposition à un AVAsset (et vous ne pouvez pas get un NSURL directement depuis AVComposition) .

Vous ne souhaitez probablement pas utiliser de méthode de téléchargement qui AFHTTPRequestOperation ou les API associées, car elles sont basées sur l'API NSURLConnection obsolète par opposition aux API NSURLSession plus modernes. NSURLSession vous permettra d'effectuer des videos longues en cours d'exécution dans un process en arrière-plan, permettant à vos users de quitter votre application et d'être sûr que le téléchargement se terminera malgré tout.

Dans ma réponse originale, je mentionne VimeoUpload . C'est une bibliothèque qui gère les téléchargements de files video vers Vimeo, mais son kernel peut être réutilisé pour gérer les téléchargements de files video d'arrière-plan simultanés vers n'importe quelle destination. Divulgation complète: Je suis l'un des auteurs de la bibliothèque.