iOS: Select a GIF from the photo library, convert to NSData for use in multipart/form-data

前端 未结 4 1632
孤城傲影
孤城傲影 2020-12-05 05:58

What\'s currently working in my code:

I select a JPG or PNG from the Photo Library (using standard ImagePicker methods), and convert that image to NSData using:

相关标签:
4条回答
  • 2020-12-05 06:15

    Here's a version that uses the newer Photos framework:

    - (void) imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
    
        NSURL * refUrl = [info objectForKey:UIImagePickerControllerReferenceURL];
        if (refUrl) {
            PHAsset * asset = [[PHAsset fetchAssetsWithALAssetURLs:@[refUrl] options:nil] lastObject];
            if (asset) {
                PHImageRequestOptions *options = [[PHImageRequestOptions alloc] init];
                options.synchronous = YES;
                options.networkAccessAllowed = NO;
                options.deliveryMode = PHImageRequestOptionsDeliveryModeHighQualityFormat;
                [[PHImageManager defaultManager] requestImageDataForAsset:asset options:options resultHandler:^(NSData * _Nullable imageData, NSString * _Nullable dataUTI, UIImageOrientation orientation, NSDictionary * _Nullable info) {
                    NSNumber * isError = [info objectForKey:PHImageErrorKey];
                    NSNumber * isCloud = [info objectForKey:PHImageResultIsInCloudKey];
                    if ([isError boolValue] || [isCloud boolValue] || ! imageData) {
                        // fail
                    } else {
                        // success, data is in imageData
                    }
                }];
            }
        }
    }
    
    0 讨论(0)
  • 2020-12-05 06:19

    Here's Eli's version using Swift 3:

    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String: Any]) {
        guard let imageURL = info[UIImagePickerControllerReferenceURL] as? URL else { return }
        guard let asset = PHAsset.fetchAssets(withALAssetURLs: [imageURL], options: nil).lastObject else { return }
    
        if picker.sourceType == .photoLibrary || picker.sourceType == .savedPhotosAlbum {
            let options = PHImageRequestOptions()
            options.isSynchronous = true
            options.isNetworkAccessAllowed = false
            options.deliveryMode = .highQualityFormat
            PHImageManager.default().requestImageData(for: asset, options: options) { data, uti, orientation, info in
                guard let info = info else { return }
    
                if let error = info[PHImageErrorKey] as? Error {
                    log.error("Cannot fetch data for GIF image: \(error)")
                    return
                }
    
                if let isInCould = info[PHImageResultIsInCloudKey] as? Bool, isInCould {
                    log.error("Cannot fetch data from cloud. Option for network access not set.")
                    return
                }
    
                // do something with data (it is a Data object)
            }
        } else {
            // do something with media taken via camera
        }
    }
    
    0 讨论(0)
  • 2020-12-05 06:31

    Swift 4 update:

    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
        let mediaType = info[UIImagePickerControllerMediaType] as! CFString
        let assetPath = info[UIImagePickerControllerReferenceURL] as! URL
    
        self.dismiss(animated: true, completion: nil)
        switch mediaType {
            case kUTTypeImage, kUTTypeLivePhoto, kUTTypeGIF:
                if let selectedImage = info[UIImagePickerControllerOriginalImage] as? UIImage {
                    if (assetPath.absoluteString.hasSuffix("GIF")) || (assetPath.absoluteString.hasSuffix("gif")){
                        print("GIF")
                        let options = PHImageRequestOptions()
                        options.isSynchronous = true
                        options.isNetworkAccessAllowed = false
                        options.deliveryMode = .highQualityFormat
    
                        guard let asset = PHAsset.fetchAssets(withALAssetURLs: [assetPath], options: nil).lastObject else { return }
                        PHImageManager.default().requestImageData(for: asset, options: options) { data, uti, orientation, info in
                        guard let info = info else { return }
    
                        if let error = info[PHImageErrorKey] as? Error {
                            print("Cannot fetch data for GIF image: \(error)")
                            return
                        }
    
                        if let isInCould = info[PHImageResultIsInCloudKey] as? Bool, isInCould {
                           print("Cannot fetch data from cloud. Option for network access not set.")
                            return
                        }
                        if let gifImageData = data {
                            // do something with data (it is a Data object)
                        }
                    }
                } else {
                    // this is the basic image 
                }
            }
            case kUTTypeMovie:
                if let videoURL = info[UIImagePickerControllerMediaURL] as? URL {
                    // using video type
                }
            default:
                print("Others")
            }
        }
    
    0 讨论(0)
  • 2020-12-05 06:37

    The UIImagePickerControllerReferenceURL key doesn't appear until iOS 4.1. I therefore take it as implicit in your question that it's fine to use the AssetsLibrary framework, which appeared in iOS only at 4.0. In which case, you can use the following:

    - (void)imagePickerController:(UIImagePickerController *)picker 
            didFinishPickingMediaWithInfo:(NSDictionary *)info
    {
        ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
        [library assetForURL:[info objectForKey:UIImagePickerControllerReferenceURL]
            resultBlock:^(ALAsset *asset)
            {
                ALAssetRepresentation *representation = [asset defaultRepresentation];
                
                NSLog(@"size of asset in bytes: %d", [representation size]);
                
                unsigned char bytes[4];
                [representation getBytes:bytes fromOffset:0 length:4 error:nil];
                NSLog(@"first four bytes: %02x (%c) %02x (%c) %02x (%c) %02x (%c)",
                                   bytes[0], bytes[0], 
                                   bytes[1], bytes[1], 
                                   bytes[2], bytes[2], 
                                   bytes[3], bytes[3]);
    
                [library autorelease];
            }
            failureBlock:^(NSError *error)
            {
                NSLog(@"couldn't get asset: %@", error);
    
                [library autorelease];
            }
        ];
    }
    

    So, you create an ALAssetsLibrary, ask it to find you the asset with the URL specified (it understands the assets-library:// URL scheme), then when you get the asset you grab its default representation and use that to feed you the bytes. They'll be the actual on-disk bytes, the default representation for an asset from the library being its on-disk form.

    For example, selecting a particular GIF I grabbed at random from Google images, from an image picker wired up to a delegate with that method in it gives me the output:

    2011-03-03 23:17:37.451 IPTest[1199:307] size of asset in bytes: 174960

    2011-03-03 23:17:37.459 IPTest[1199:307] first four bytes: 47 (G) 49 (I) 46 (F) 38 (8)

    So that's the beginning of the standard GIF header. Picking PNGs or JPGs gives the recognisable first four bytes of the PNG and JPG headers.

    EDIT: to finish the thought, obviously you can use ALAssetRepresentation to read all of the bytes describing the file into a suitably malloc'd C array, then use NSData +(id)dataWithBytes:length: (or, more likely, +dataWithBytesNoCopy:length:freeWhenDone:) to wrap that into an NSData.

    0 讨论(0)
提交回复
热议问题