9

現在、次のコードを使用して動画をアップロードしています。

  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エミュレーターで機能する代わりに、メソッドを使用してファイルパスを要求しようとしまし appendPartWithFileData た。実際のデバイスでは、指定されたパスでファイルを読み取れないというエラーで失敗します。この問題については、 PHAsset + AFNetworking で説明しました。実デバイスのサーバーにファイルをアップロードできません

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

更新:次のように、新しい 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);

        }



    }];
}

したがって、問題は同じままです-サーバーにアップロードされたファイルは空です

4

3 に答える 3

12

上記の提案された解決策は、部分的にしか正しいものではありません (そして、以前に私が見つけたものです)。システムはサンドボックス外のファイルを読み取ることを許可していないため、ファイル パスによってファイルにアクセス (読み取り/書き込み) できず、コピーされるだけです。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

このアップロード方法には重大な欠点があります..デバイスが「スリープモード」に入ると、あなたは台無しになります..したがって、ここで推奨されるアップロード方法は、方法を使用することです。uploadTaskWithRequest:fromFile:progress:completionHandlerAFURLSessionManager.

iOS 9未満のバージョンの場合..画像の場合、私の質問のコードに示されているように..NSDATAから取得してアップロードできます。PHAssetまたは、アップロードする前にアプリのサンドボックス ストレージに最初に書き込みます。このアプローチは、大きなファイルの場合には使用できません。または、画像/ビデオ ピッカー エクスポート ファイルを .xml として使用することもできますALAssetALAssetストレージからファイルを読み取ることができる API を提供しますが、アップロードする前にサンドボックス ストレージに書き込む必要があります。

于 2015-11-06T02:12:44.387 に答える
3

ここであなたの他の質問に対する私の答えを見てください。主題がリンクされているため、ここで関連しているようです。

その回答では、私の経験では、Apple は PHAsset に関連付けられたビデオ ソース ファイルへの直接アクセスを許可していないことを説明しています。またはそのことについてはALAsset。

ビデオ ファイルにアクセスしてアップロードするには、まずAVAssetExportSession. または、iOS9+ PHAssetResourceManager API を使用します。

OOM 例外がすぐに発生するため、メモリにデータをロードするメソッドは使用しないでください。また、AVAsset ではなく AVComposition を取得することがあるため、上記の方法を使用するrequestAVAssetForVideo(_:options:resultHandler:)ことはお勧めできません (また、AVComposition から直接 NSURL を取得することはできません)。

また、APIを活用または関連するアップロード方法を使用したくない場合もあります。これらの方法は、最新のNSURLSessionAFHTTPRequestOperation APIではなく、非推奨の NSURLConnection API に基づいているためです。NSURLSession を使用すると、長時間実行される動画のアップロードをバックグラウンド プロセスで実行できるため、ユーザーはアプリを離れてもアップロードが確実に完了することを確信できます。

私の元の回答では、VimeoUploadについて言及しています。Vimeo へのビデオ ファイルのアップロードを処理するライブラリですが、そのコアを再利用して、任意の宛先への同時バックグラウンド ビデオ ファイルのアップロードを処理できます。完全な開示: 私はライブラリの作成者の 1 人です。

于 2016-03-16T19:47:30.937 に答える