PHAsset + AFNetworking. Unable to upload files to

2019-02-07 13:55发布

问题:

Currently I'm using the following code to upload files to the server

NSURLRequest *urlRequest =  [[AFHTTPRequestSerializer serializer] multipartFormRequestWithMethod:@"POST" URLString:[[entity uploadUrl]absoluteString] parameters:entity.params constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {

    // Get file url 
    [UploadModel getAassetUrl:entity.asset resultHandler:^(NSURL *fileUrl) {

    NSError *fileappenderror;
    // Append
                [formData appendPartWithFileURL:fileUrl name:@"data" error:&fileappenderror];

                if (fileappenderror) {
                  [Sys MyLog: [fileappenderror localizedDescription] ];
                }
            }];

        } error:&urlRequestError];

/*getAassetUrl */

+(void)getAassetUrl: (PHAsset*)mPhasset resultHandler:(void(^)(NSURL *imageUrl))dataResponse{

    PHImageRequestOptions * requestOption = [[PHImageRequestOptions alloc] init];
    requestOption.synchronous = YES;
    requestOption.deliveryMode = PHImageRequestOptionsDeliveryModeFastFormat;

        [[PHImageManager defaultManager] requestImageDataForAsset:mPhasset options:requestOption resultHandler:^(NSData *imageData, NSString *dataUTI, UIImageOrientation orientation, NSDictionary *info) {

        dataResponse([info objectForKey:@"PHImageFileURLKey"]);

    }];
}

This approach works on a simulator, but fails on a real device: empty files are uploaded to the server most likely due to failure to read from the local storage. Log shows the notice

Notice: Sandbox: MyApp(213) deny file-read-data /private/var/mobile/Media/DCIM/101APPLE/IMG_1570.PNG

I believe this note means that app can't access the file by specified path. Also I've tried an alternative approach uploading file by appending with NSData which is returned from request PHAsset data. but this approach is unusable in case of large media files. since the entire file is loaded into the memory.

Any thoughts?

回答1:

The NSData object returned by requestImageDataForAsset is memory mapped - so the entire file is not loaded into memory. So this method will for without any issues for images.

For videos you should use the appropriate methods requestExportSessionForVideo or requestAVAssetForVideo If you can limit your deployment target to iOS 9, you should also take a look at the methods of PHAssetResourceManager



回答2:

You shouldn't use requestImageDataForAsset(_:options:resultHandler:) for large files. Reason being you don't want to load the entire media file into memory, you will quickly run out of memory and the app will crash. This typically means you shouldn't use it for large images or pretty much any video.

In my experience, attempting to upload directly from a PHAsset resource url will fail. Apple doesn't seem to grant us the permissions required to upload direct from PHAsset source files. See forum post here. This is a pain because it forces us to use a ton of extra disk space if we want to upload a video.

In order to get a local file url for a video file that you intend to upload, you'll want to use either:

requestExportSessionForVideo(_:options:exportPreset:resultHandler:)

or

requestAVAssetForVideo(_:options:resultHandler:)

You will use these methods to export a copy of the video file to a location on disk that you control. And upload from that file. Bonus feature: both of these methods will download the file from iCloud if necessary.

Check out the VimeoUpload library for details on all things related to video upload. Disclaimer: I'm one of the authors of the library.

Even if you're not uploading to Vimeo servers, you can use the PHAssetExportSessionOperation and ExportOperation classes included in VimeoUpload to do exactly what you're looking to do. See the repo README for details on obtaining a file url for a PHAsset. It also includes tools for obtaining a file url for an ALAsset.

If you're not interested in using PHAssetExportSessionOperation or ExportOperation, check out their implementations for details on how to use the Apple classes under the hood.