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

  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(aka filedata)上传文件(请参见上面的方法)。我尝试使用方法请求文件路径appendPartWithFileURLappendPartWithFileData接口(interface)
它可以在模拟器上运行。并在实际设备上失败,并显示错误消息,即无法通过指定的路径读取文件。我已经在这里描述了这个问题
PHAsset + AFNetworking. Unable to upload files to the server on a real device

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

更新:我已经修改了我的代码,以测试通过新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];

在iPhone 6s +上进行测试可提供更清晰的日志警告,它是由于调用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.

这是用于从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);

        }



    }];
}

因此问题仍然存在-上传到服务器的文件为空

最佳答案

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

首先,将文件复制到应用程序沙箱目录。

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

此上载方法有一个很大的缺点..如果设备进入“ sleep 模式”,您会很费劲。因此,此处推荐的上载方法是使用method。 uploadTaskWithRequest:fromFile:progress:completionHandler中的AFURLSessionManager.
对于iOS 9以下的版本。如果是图像,您可以按照问题代码中的说明从NSDATA提取PHAsset并上传。或先将其写入应用沙箱存储,然后再上传。在大文件的情况下,此方法不可用。另外,您可能希望使用“图像/视频选择器”将文件导出为ALAssetALAsset提供了api,它允许您从存储中读取文件。但是在上传之前,您必须将其写入沙盒存储中。

10-07 19:41
查看更多