Nativescript: ImageSourceModule method toBase64String not working (both iOS and Android)

Created on 8 Feb 2017  路  25Comments  路  Source: NativeScript/NativeScript

On line 140 of image-source-android.js this.android.compress(targetFormat, quality, base64Stream); I'm getting this error this.android.compress is not a function. I'm using the nativescript-camera to take a picture and then save it to base64 string. Also, the image is saving to my photos even when I set saveToGallery: false.

Here my code:

cameraModule.takePicture({
        width: 1280, height: 720, keepAspectRatio: false, saveToGallery: false
    }).then(function (imageAsset) {
        console.log("Result is an image asset instance");
        viewModel.set("BoardingPassSource", imageAsset);
        var image = ImageSourceModule.fromNativeSource(imageAsset);
        var base64 = image.toBase64String("jpeg");
        // var image = new imageModule.Image();
        // image.imageSource = imageAsset;
        // image.toBase64String("jpeg");
        viewModel.set("base64String", base64);
    }).catch(function (err) {
        console.log("Error -> " + err.message);
    });

package.json

{
  "description": "NativeScript Application",
  "license": "SEE LICENSE IN <your-license-filename>",
  "readme": "NativeScript Application",
  "repository": "<fill-your-repository-here>",
  "nativescript": {
    "id": "org.nativescript.****",
    "tns-android": {
      "version": "2.5.0"
    }
  },
  "dependencies": {
    "nativescript-background-http": "^2.4.2",
    "nativescript-barcodescanner": "^2.3.3",
    "nativescript-camera": "0.0.8",
    "nativescript-drawingpad": "^1.1.2",
    "nativescript-drop-down": "^1.5.1",
    "nativescript-loading-indicator": "^2.2.2",
    "nativescript-telerik-ui": "^1.5.1",
    "nativescript-theme-core": "^1.0.2",
    "nativescript-timedatepicker": "^1.1.0",
    "tns-core-modules": "^2.5.0"
  },
  "devDependencies": {
    "babel-traverse": "6.21.0",
    "babel-types": "6.21.0",
    "babylon": "6.14.1",
    "lazy": "1.0.11"
  }
}
backlog high

Most helpful comment

I found another alternative!

cameraModule.takePicture({\
        width: 1280, height: 720, keepAspectRatio: false, saveToGallery: false
    }).then(function (imageAsset) {
        imageAsset.getImageAsync(function (res){
            console.log(res);
            viewModel.set("BoardingPassSource", imageSource); 
            var image = ImageSourceModule.fromNativeSource(imageSource);
            viewModel.set("base64String", image.toBase64String('png'));
            // var stream;
            // var comp = image.android.compress("png", 70, stream);
            console.log("image selected"); 
        });
    }).catch(function (err) {
        console.log("Error -> " + err.message);
    });    

now I want to find a way to compress the ImageAsset or the ImageSource.
Any suggestions? @pap5508 @NickIliev

All 25 comments

Hi @emmanuel128

I can confirm this issue is reproducible with tns-core-modules 2.5.0. We will get back at you in this thread once we have applicable solution and/or fix.

Test application demonstrating the issue can be found here.

Throwing at this line in tns-core-modules as this.android is ImageAsset and not android.graphics.Bitmap

Hi @NickIliev

Thanks for trying to fix this! If this is a problem with tns-core-modules 2.5.0, I'm able to convert the result of nativescript-camera to a base64 string? That is the biggest challenge I'm having right now :/

Update:
I use the same instruction but with the built-in camera module and it works perfectly, but this module is now deprecated.

Tested with _modules30_ and works as expected

How can I test it?

@NickIliev : this issue is resolved..??

Hey @emmanuel128 , @pap5508
The issue is resolved with our next release 3.0 - however, it is a major version release coming with some breaking changes so we are still working on it. More about the upcoming release can be found here.

@NickIliev : Is there any alternative to convert it into Base64 string.. because I would like to convert to base64 and send Image..
I am stuck with this Base64 encoding issue on iOS.. I am not able to go further.
when Nativescript releasing 3.0 ?

I used the nativescript-camera and nativescript-imagepicker to take a picture, save it and convert it to base 64. I hope it helps. If anyone has another workaround please let me know.

    // nativescript-camera
    cameraModule.requestPermissions();
    cameraModule.takePicture({
        width: 1280, height: 720, keepAspectRatio: false, saveToGallery: true
    }).then(function (imageAsset) {
        console.log("Result is an image asset instance");

        var context = imagepicker.create({
            mode: "single"
        });

        context
        .authorize()
        .then(function() {
            return context.present();
        })
        .then(function(selection) {
            console.log("Selection done:");
            selection.forEach(function(selected) {
                selected.getImage().then(res =>{
                    var d = new Date();
                    _photoDateTime = d.getFullYear()+"-"+(d.getMonth()+1)+"-"+d.getDate()+" "+
                                     d.getHours()+":"+d.getMinutes()+":"+d.getSeconds();
                    viewModel.set("PhotoDateTime", _photoDateTime);

                    viewModel.set("BoardingPassSource", res); 
                    viewModel.set("base64String", res.toBase64String("png"));
                    console.log("image selected");                    
                });
            });
        }).catch(function (e) {
            console.log(e);
        });

    }).catch(function (err) {
        console.log("Error -> " + err.message);
    });

@emmanuel128 : One more Question on Which I required your guidance..
Are you using any image compression library or tool.. Because in my Chat application.. Load all chat containing base64 string and convert in into ImageSource and set it to Image takes lot of time..to upload as well as to to download chat history..

If you are using such tools or any other which help me out on this issue..

Thank you..!!

No, I'm not using any tool, I just lower the device photo quality to 1 MP.

I searched but didn't find anything to compress the image or the base64 string. If you know @pap5508 or @NickIliev know any please let me know.

@emmanuel128 : how you define photo quality to 1MP in iOS..?

@pap5508 I do it on android on the phone camera settings, not in the code.

The issue still reproduces with {N} 3.0.0 RC.

This also appears to happen when you call saveToFile on android as well. The following code crashes at var saved...

var folder = android.os.Environment.getExternalStoragePublicDirectory(android.os.Environment.DIRECTORY_PICTURES);
            if (!folder.isDirectory()) {
                try {
                    folder.mkdir();
                } catch (e) {
                    e.printStackTrace();
                }
            }
            var fileName = 'img_' + new Date().getTime() + '.jpg';
            var path = folder + '/' + fileName;
            var imageSource = imageSourceModule.fromNativeSource(webImage.android);
            var saved = imageSource.saveToFile(path, enums.ImageFormat.jpeg);

Hey, @davecoffin keep in mind that for Android API23 and above you will need explicit permissions (given runtime) to have access to the external storage and to be able to save images. While for lower API versions you can add those permissions in the AndroidManifest.xml file for API23 and above you will need them once in your AndroidManifest.xml and then you should ask for them when needed.

Here you can find an example application recreating your scenario and using nativescript-permissions to grant the permissions on Page loaded event.

Thanks! My problem ended up being that I wasnt passing an acceptable image object. I was trying to derive the native source from a Web Image object from this plugin: https://www.npmjs.com/package/nativescript-web-image-cache but couldnt figure it out. So I got the image source from the URL. More on that here: https://discourse.nativescript.org/t/how-to-save-an-imagesource-object-to-the-devices-photo-library-on-both-ios-and-android/1126

I found another alternative!

cameraModule.takePicture({\
        width: 1280, height: 720, keepAspectRatio: false, saveToGallery: false
    }).then(function (imageAsset) {
        imageAsset.getImageAsync(function (res){
            console.log(res);
            viewModel.set("BoardingPassSource", imageSource); 
            var image = ImageSourceModule.fromNativeSource(imageSource);
            viewModel.set("base64String", image.toBase64String('png'));
            // var stream;
            // var comp = image.android.compress("png", 70, stream);
            console.log("image selected"); 
        });
    }).catch(function (err) {
        console.log("Error -> " + err.message);
    });    

now I want to find a way to compress the ImageAsset or the ImageSource.
Any suggestions? @pap5508 @NickIliev

@emmanuel128 so do i锛宒id you find the solution?

I am having the same issue.
Thanks to @emmanuel128 reached as far as he reached, and I am using the latest modules as mentioned below:
nativescript -- 3.0.1
tns-core-modules -- 3.0.1
tns-android -- 3.0.0

Getting error: TypeError: this.android.compress is not a function
/tns-core-modules/image-source/image-source.js line: 141, column:21.

i have solved my problem

 startSelection(context, pageObj, imageItems, cb) {
        var _this = this;
        imageItems = imageItems ? imageItems : this.imageItems;
        context
            .authorize()
            .then(function () {
                // imageItems.length = 0;
                return context.present();
            })
            .then(function (selection) {
                selection.forEach(function (selected_item) {

                    selected_item.getImage().then(function (imagesource) {
                        //bad code
                      /*  var folderPath = android.os.Environment.getExternalStoragePublicDirectory(android.os.Environment.DIRECTORY_DOWNLOADS).toString();

                        // let folder = fs.knownFolders.documents();
                        var fileName = "jzt_" + new Date().getTime() + ".jpeg";
                        let path = fs.path.join(folderPath, fileName);
                        //鏌ョ湅婧愮爜淇敼
                        //https://github.com/NativeScript/NativeScript/blob/master/tns-core-modules/image-source/image-source.android.ts
                        let saved = imagesource.saveToFile(path, enums.ImageFormat.jpeg, 5);
                        if (saved) {
                            var task = _this.sendImages(path, cb);
                            var item = new observable.Observable();
                            item.set("thumb", imagesource);
                            item.set("uri", fileName);
                            item.set("uploadTask", task);
                            item.set("url", imagesource);
                            if (pageObj) {
                                pageObj.bindingContext.set('srcThumb', imagesource);
                            }
                            imageItems.push(item);
                        }
                        _this.counter++;*/

                        //good code
                        var localPath=null;

                        if (platformModule.device.os === "Android") {
                            localPath = selected_item.android;
                        } else {
                            localPath = selected_item.ios;
                        }

                        if (localPath) {
                            var task = _this.sendImages(localPath, cb);
                            var item = new observable.Observable();
                            item.set("thumb", imagesource);
                            item.set("uri", localPath);
                            item.set("uploadTask", task);
                            item.set("url", imagesource);
                            if (pageObj) {
                                pageObj.bindingContext.set('srcThumb', imagesource);
                            }
                            imageItems.push(item);
                        }


                    })

                });
            }).catch(function (e) {
                console.log(e.eventName);
            });

see:https://gist.github.com/giscafer/30ac4883f12dd3809d00926618925305

pull request:https://github.com/NativeScript/sample-ImageUpload/pull/14

+1 from t.1125152

@emmanuel128
Where is viewModel defined?

var image = ImageSourceModule.fromNativeSource(imageAsset);

you need to assign nativeImage to this method.

change it to:

var image = ImageSourceModule.fromNativeSource(imageAsset.nativeImage);

Hello @emmanuel128, @pap5508 and All,

The API could be misleading here and as @emmanuel128 suggested, the proper way to implement this is as follow:

cameraModule.takePicture({
        width: 1280, height: 720, keepAspectRatio: false, saveToGallery: false
    }).then(function (imageAsset) {
        console.log("Result is an image asset instance");
        viewModel.set("BoardingPassSource", imageAsset);
        imageAsset.getImageAsync(image => {
            let imageSource = ImageSourceModule.fromNativeSource(image);
            let encodedString = imageSource.toBase64String("jpeg");
            console.log(encodedString);
        })
    }).catch(function (err) {
        console.log("Error -> " + err.message);
    });

I opened https://github.com/NativeScript/NativeScript/pull/5273 in order to throw if the source is not a correct native instance.
A sample application could be found here.

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

minjunlan picture minjunlan  路  3Comments

kn9ts picture kn9ts  路  3Comments

Pourya8366 picture Pourya8366  路  3Comments

valentinstoychev picture valentinstoychev  路  3Comments

rogangriffin picture rogangriffin  路  3Comments