natario
natario

Reputation: 25194

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:

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:

Upvotes: 7

Views: 926

Answers (2)

natario
natario

Reputation: 25194

The answer above was correct and solved my issue, but I would be a little more careful in giving the advice of relying on "runtime-width" images. For some (most?) uses, it is definitely better to use pre-scaled images hosted on the server.

There are two issues here:

  1. API requests:

Parse charges you based on the API network requests you do in a second. This is not an issue: either you call callFunctionInBackground(), or loadInBackground() from a ParseImageView, it is the same thing - one API request per call. But.

  1. Caching:

If you have a server-saved ParseFile, you can cache it in a second. If you have a downloaded Bitmap that was just created for you, you can NOT cache it - not with the Parse SDK, anyway. You could try putting this ParseFile in a ParseObject and pin it, but it doesn't work - pinning requires ParseFiles to exist in the server. You could try putting the image byte[] in a ParseObject field, but this fails with big images and I didn't manage to make it work.

If you don't cache, things get bed soon - setting up a RecyclerView is a pain, and each time a view holder is bind, you spend a new API request.

So - I would discourage using this approach of custom-width images with adapter views and recycler views. If you go down that road, make sure you have an image caching library on your side.

Upvotes: 0

Wand Maker
Wand Maker

Reputation: 18762

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

Upvotes: 1

Related Questions