Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Download image through Parse Cloud Code function

I'm developing an app that deals with pictures and using parse.com services as a backend. At some point I had to choose between:

  • store different versions of the same picture, e.g. 100x100 for thumbnails, 400x400 for bigger views, 1000x1000 for fullscreen views;
  • store only the 1000x1000 version, and scale it down when needed, possibly server-side.

The solution I'm currently working on is a mixture of the two: I hold 100x100 for thumbnails, 1000x1000 for fullscreen views and would like to scale it down for any other need. I started working on a Cloud Code function to achieve this. My wish is to pass to the function the width of the current view, so to make the image adaptable to the client's need.

var Image = require("parse-image");

Parse.Cloud.define("getPicture", function(request, response) {

  var url = request.params.pictureUrl;
  var objWidth = request.params.width / 2;

  Parse.Cloud.httpRequest({
    url: url

  }).then(function(resp) {
    var i = new Image();
    return i.setData(resp.buffer);

  }).then(function(i) {
    var scale = objWidth / i.width();
    if (scale >= 1) {
        response.success(i.data());
    }
    return i.scale({
      ratio: scale
    });

  }).then(function(i) {
    return i.data();

  }).then(function(data) {
    response.success(data);
  });

});

I have two questions:

  • is this approach correct, or should I better simply store a middle-sized version of the image (like 400x400)? Would this determine too many calls to the cloud code function? (I'm not aware of any parse.com restriction for the number of cloud functions calls, but there might be)

  • What kind of object is i.data() returning, and how can I get a Bitmap from it? From the Android app I'm calling:

    HashMap<String, Object> params = new HashMap<>();
    params.put("pictureUrl",getUrl());
    params.put("width", getWidth());
    ParseCloud.callFunctionInBackground("getPicture", params, new FunctionCallback<Object>() {
        @Override
        public void done(Object object, ParseException e) {
    
            //here I should use BitmapFactory.decodeByteArray(...)
            //but object is definitely not a byte[] !
            //From debugging it looks like a List<Integer>,
            //but I don't know how to get a Bitmap from it.
    
        }
    });
    
like image 957
natario Avatar asked Aug 12 '15 12:08

natario


1 Answers

Approach looks good.

Since the return value of Parse API is JSON, only way you can send binary data (an image) back is either as JSON Integer array or by using either Hex or Base64 encoded value of the binary data.

You can return base64 string by using Buffer#toString() method of Parse Cloud as shown below. base64 strings are smaller in size when compared to hex encoding, hence, are preferred.

response.success(data.toString('base64'));

On the Android side, code given below can be used which decodes the base 64 string into byte[] so that can be used in Bitmapfactory#decodeByteArray.

ParseCloud.callFunctionInBackground("getPicture", params,
    new FunctionCallback<String>() {
        @Override
        public void done(String object,
                ParseException e) {

            byte[] imgBytes = Base64.decode(object, Base64.DEFAULT);
            if (imgBytes.length > 0) {
                Bitmap bitmap = BitmapFactory.decodeByteArray(imgBytes, 0, imgBytes.length);
                ((ImageView) findViewById(R.id.image_view)).setImageBitmap(bitmap); 
            }
        }

    }
);

I have tested the changes proposed above, they work just fine.

Note: Please note that R.id.image_view is for reference purpose, you will have to use id of ImageView as applicable to your project

like image 161
Wand Maker Avatar answered Oct 21 '22 04:10

Wand Maker