2

I'm trying to upload images to Firebase like this:

Firebase *ref = [[Firebase alloc] initWithUrl:@"https://<app-name>.firebaseio.com/posts"];
Firebase *newPost = [ref childByAutoId];

NSDictionary *newPostData = @{
                              @"image" : [self encodeToBase64String:image]
                              };
[newPost updateChildValues:newPostData];

I'm using this code to encode the image:

- (NSString *)encodeToBase64String:(UIImage *)image {
return [UIImagePNGRepresentation(image) base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength];
}

But this does not work as the string exceeds the maximum size:

Terminating app due to uncaught exception 'InvalidFirebaseData', reason: '(updateChildValues:) String exceeds max size of 10485760 utf8 bytes:

What can I do to resolve this problem? I haven't found anything online in regards to iOS development and images when using Firebase.

Erik
  • 2,500
  • 6
  • 28
  • 49

2 Answers2

6

If the image is too big, you should store a smaller image. Let me quote myself: How do you save a file to a Firebase Hosting folder location for images through android?

The Firebase Database allows you to store JSON data. While binary data is not a type that is supported in JSON, it is possible to encode the binary data in say base64 and thus store the image in a (large) string. [But] while this is possible, it is not recommended for anything but small images or as a curiosity to see that it can be done.

Your best option is typically to store the images on a 3rd party image storage service.

Community
  • 1
  • 1
Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • I used this approach. Storing the image itself in Amazon S3, and a reference to it in Firebase – Erik Feb 07 '16 at 12:48
  • Cool. Can you share some of the crucial code as an answer? That would be extremely useful to others. – Frank van Puffelen Feb 07 '16 at 15:37
  • Can I just ask you a quick question here without opening a new SO question? I'm removing values, but as I need to remove values at multiple paths - do I for instance have to do a for-loop and run .remove() many times, or is there a more efficient way like `updateChildValues` when adding values? – Erik Feb 09 '16 at 09:25
  • If you update a path with a `null` value, any existing value at that path will be removed. So `update({ "path/to/first": null, "another/path": null })` will remove two values. – Frank van Puffelen Feb 09 '16 at 14:10
  • `NSMutableDictionary`'s `setObject:` specifies it wants a nonull value, can I still do `setObject:NULL` in obj-c? – Erik Feb 12 '16 at 09:38
  • 1
    Figured it out with `NSNull` – Erik Feb 12 '16 at 09:48
3

As Frank van Puffelen suggested, my solution was to use Amazon S3 for imagine storage, and use Firebase to store a reference to the image location.

I created a method called uploadImage: and it looks like this:

-(void)uploadImage:(UIImage *)image
{
// Create reference to Firebase
Firebase *ref = [[Firebase alloc] initWithUrl:@"https://<MY-APP>.firebaseio.com"];
Firebase *photosRef = [ref childByAppendingPath:@“photos];
Firebase *newPhotoRef = [photosRef childByAutoId];

// Image information
NSString imageId = [[NSUUID UUID] UUIDString];

// Create dictionary containing information
NSDictionary photoInformation = @{
                                @“photo_id” : imageId
                                // Here you can add more information about the photo
                                };

NSString *imagePath = [NSTemporaryDirectory() stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.png", imageId]];
NSData *imageData = UIImagePNGRepresentation(image);
[imageData writeToFile:imagePath atomically:YES];

NSURL *imageUrl = [[NSURL alloc] initFileURLWithPath:imagePath];
AWSS3TransferManagerUploadRequest *uploadRequest = [AWSS3TransferManagerUploadRequest new];
uploadRequest.bucket = @“<AMAZON S3 STORAGE NAME>“; // create your own by setting up an account on Amazon aws.
uploadRequest.key = imageId;
uploadRequest.contentType = @"image/png";
uploadRequest.body = imageUrl;

AWSS3TransferManager *transferManager = [AWSS3TransferManager defaultS3TransferManager];
[[transferManager upload:uploadRequest] continueWithExecutor:[AWSExecutor mainThreadExecutor] withBlock:^id(AWSTask *task) {
    if (!task.error) {
        // Update Firebase with reference
        [newPhotoRef updateChildValues:currentPHD withCompletionBlock:^(NSError *error, Firebase *ref) {
            if (!error) {
                [newPhotoRef updateChildValues:photoInformation withCompletionBlock:^(NSError *error, Firebase *ref) {
                    if (!error) {
                        // Uploaded image to Amazon S3 and reference to Firebase
                    }
                }];
            }
        }];
    } else {
        // Error uploading
}
return nil;
}];
}

Edit The method should be a block method, something like this:

-(void)uploadImage:(UIImage *)image withBlock:(void (^)(Firebase *ref, NSError *error, AWSTask *task))handler
{
    // upload
}
Erik
  • 2,500
  • 6
  • 28
  • 49
  • This was the first method to upload images I made, I have since changed this to a block method, as well as performing the Firebase updates in one update, rather than two different. This can be achieved with multi-location updates where you for instance create a dictionary like this: `NSDictionary *firebaseUpdate = @{first_path : first_update, second_path : second update}` – Erik Feb 19 '16 at 07:06