Phasset + AfNetworking上传多个视频

时间:2015-10-22 10:11:12

标签: ios objective-c iphone afnetworking phasset

目前,我使用以下代码上传视频:

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

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

                }
            }];
    }

这种方法的问题是,无论何时上传大型/多个视频文件,应用程序都会耗尽内存。我想这是因为请求NSDATA(又名filedata)上传文件(参见上面的方法)。我尝试使用方法请求文件路径 appendPartWithFileURL {} {}} 它适用于模拟器。并且在具有错误的真实设备上失败,该错误无法通过指定的路径读取文件。我在这里描述了这个问题 PHAsset + AFNetworking. Unable to upload files to the server on a real device

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

更新:我已经修改了我的代码,以测试通过新iPhone 6s +上的本地路径上传文件的方法,如下所示

appendPartWithFileData

在iPhone 6s +上进行测试会提供更清晰的日志警告它是因为调用方法 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]; 而发生的

appendPartWithFileURL

以下是用于从PHAsset

获取本地文件路径的代码
 <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.

所以问题仍然存在 - 上传到服务器的文件是空的

3 个答案:

答案 0 :(得分:12)

上面提出的解决方案只是部分正确(并且之前我自己找到了)。由于系统不允许读取沙箱之外的文件,因此文件路径无法访问(读/写)文件,只是复制了文件。在iOS 9及更高版本中,Photos Framework提供了API(它不能通过NSFileManager完成,但只能使用Photos framework api)将文件复制到App的沙盒目录中。这是我在挖掘文档和头文件后使用的代码。

首先将文件复制到app sandbox目录。

// 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

自行上传任务

      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

这个上传方法有一个明显的缺点..如果设备进入&#34;睡眠模式&#34;你被搞砸了。因此这里推荐的上传方法是使用uploadTaskWithRequest:fromFile:progress:completionHandler AFURLSessionManager. }}

对于iOS 9以下的版本..如果是图片您可以从NSDATA中获取PHAsset,如我的问题代码所示..并上传它。或者在上传之前先将它写入您的应用沙盒存储中。对于大文件,此方法不可用。或者,您可能希望将图像/视频选择器导出文件用作ALAssetALAsset提供了api,允许您从存储中读取文件。但是您必须在上传之前将其写入沙箱存储。

答案 1 :(得分:6)

为每个视频创建NSData可能不好,因为视频(或任何其他文件)可能比设备的RAM大得多, 我建议上传为&#34; file&#34;而不是&#34;数据&#34;,如果你附加文件,它将从磁盘块发送数据并且不会尝试一次读取整个文件,尝试使用

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

还可以查看https://github.com/AFNetworking/AFNetworking/issues/828

在您的情况下,请像这样使用

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

答案 2 :(得分:3)

请参阅我的回答here您的其他问题。这里似乎与主题相关。

在该答案中,我描述了根据我的经验,Apple不会让我们直接访问与PHAsset相关的视频源文件。或者ALAsset就此而言。

要访问视频文件并上传视频文件,您必须先使用AVAssetExportSession创建副本。或者使用iOS9 + PHAssetResourceManager API。

您不应该使用任何将数据加载到内存中的方法,因为您将快速遇到OOM异常。使用上面提到的requestAVAssetForVideo(_:options:resultHandler:)方法可能不是一个好主意,因为你有时会获得AVComposition而不是AVAsset(并且你无法从AVComposition获得NSURL)直)。

您也可能不想使用任何利用AFHTTPRequestOperation或相关API的上传方法,因为它们基于弃用的NSURLConnection API而不是更现代的NSURLSession API。 NSURLSession允许您在后台进程中执行长时间运行的视频上传,允许您的用户离开您的应用并确信无论如何都能完成上传。

在我原来的回答中,我提到VimeoUpload。它是一个处理视频文件上传到Vimeo的库,但其核心可以重新用于处理并发后台视频文件上传到任何目的地。完全披露:我是图书馆作者之一。