我正在开发一个应用程序,处理图片和使用parse.com
服务作为后端。在某些时候,我不得不在以下两者之间做出选择:
- 存储同一图片的不同版本,例如
100x100
为缩略图,400x400
为较大视图,1000x1000
为全屏视图; - 只存储
1000x1000
版本,并在需要时缩小它,可能是服务器端。
我目前正在研究的解决方案是两者的混合:我持有100x100
缩略图,1000x1000
全屏视图,并希望按比例缩小任何其他需要。我开始研究云代码功能来实现这一点。我的愿望是将当前视图的宽度传递给函数,使图像适应客户端的需要。
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);
});
});
我有两个问题:
这种方法是正确的,还是我应该更好地存储一个中等大小的图像版本(如
400x400
)?这会导致对云代码函数的调用过多吗?(我不知道任何parse.com
限制云函数调用的数量,但可能有)i.data()
返回的是什么类型的对象,我如何从中获得Bitmap
?从我正在调用的Android应用程序: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. } });
方法看起来不错。
由于Parse API的返回值是JSON,您可以发送二进制数据(图像)返回的唯一方法是JSON整数数组或使用二进制数据的十六进制或Base64编码值。
可以使用Parse Cloud的Buffer#toString()方法返回base64
字符串,如下所示。与hex
编码相比,base64
字符串的大小更小,因此首选。
response.success(data.toString('base64'));
在Android端,可以使用下面给出的代码将base 64字符串解码为byte[]
,以便可以在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);
}
}
}
);
我已经测试了上面建议的更改,它们工作得很好。
注意:请注意R.id.image_view
仅供参考,您必须根据您的项目使用ImageView
中的id
上面的答案是正确的,并且解决了我的问题,但是我在给出依赖"运行时宽度"图像的建议时会更小心一些。对于某些(大多数?)用途,使用托管在服务器上的预缩放图像肯定更好。
有两个问题:
- API请求:
Parse根据您在一秒钟内执行的API网络请求向您收费。这不是一个问题:无论是你调用callFunctionInBackground()
,或loadInBackground()
从ParseImageView,这是同样的事情-每次调用一个API请求。但是。
- 缓存:
如果您有一个服务器保存的ParseFile,您可以在一秒钟内缓存它。如果你下载了一个刚刚为你创建的位图,你不能缓存它——不管怎么说,用Parse SDK是不行的。您可以尝试将这个ParseFile放在ParseObject中并固定它,但它不起作用-固定需要ParseFiles存在于服务器中。您可以尝试将图像byte[]
放在ParseObject字段中,但这对于大图像来说是失败的,并且我没有设法使其工作。
如果你不缓存,事情很快就会变糟——设置一个RecyclerView是一个痛苦的过程,而且每次绑定一个视图holder,你都要花费一个新的API请求。
所以,我不鼓励在适配器视图和回收器视图中使用这种自定义宽度图像的方法。如果你要这样做,请确保你有一个图像缓存库。