Reputation: 25194
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:
100x100
for thumbnails, 400x400
for bigger views, 1000x1000
for fullscreen views;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.
}
});
Upvotes: 7
Views: 926
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:
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.
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
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