React (native) image storing / fetching good practices - reactjs

My use case is a mobile app with react native, but I guess it's very common good practices.
I want to be able, in an app, to take an image (from the camera or the gallery), and to be able to store it so it can be fetched from the date it was added, or some metadata added by the user.
The theory seems quite simple, a way of doing it can be :
Use any library (eg this great one) to get the image,
Store image as base64 and metadata in, let's say RealmJS (some internal DB),
Query this DB to get what I want.
This should work, and should be quite simple to implement.
But I'm wondering about a few things :
According to the performance of a smartphone's camera, isn't it quite a shame to store it as base64 (and no checksum, more memory used, ...) ?
This format, base64, isn't a bad idea in general for storing image ?
Is it a good idea to store the image in RealmJS, as it will be a pain for the user to reuse the image (share it on facebook...), but on the other hand, if I wrote it to the smartphone and store a URI, it can lead to a lot of problems (missing file if the user deletes it, need to access to memory, ...)
Is this approach "clean" (ok it works, but ...) ?
If you have any experience, tips, or good practice to share, I'll be happy to talk about it :)

You can store binary data (images) in Realm. But if you are using Realm locally (not sync), I will suggest that you store the image on the file system and store the path in Realm. Your model could be something like:
const ImageSchema = {
name: 'Image',
properties: {
path: 'string',
created: 'Date',
modified: 'Date?',
tags: 'Tag[]'
}
};
const TagSchema = {
name: 'Tag',
properties: {
name: 'string',
images: { type: 'linkingObjects', objectType: 'Image', property: 'tags' }
}
};
That is, for every image the timestamp for its creation is stored. Moreover, it has an optional timestamp if the image has been modified. The property path is where to find the image. If you prefer to store the image, you can use a property of type data instead. To find image less that a week old, you can use realm.objects('Image').filtered('created >= $1', new Date(Date.now()-7*24*60*60)).
Just for fun, I have added a list of tags for each image. The linkingObject in Tag makes it possible to find all image which have a particular tag e.g., realm.objects('Tag').filtered('#links.Tag.name == "Dog"').

Related

Scala Play Framework image upload with Angular ng-file-upload

I am using Angular ng-file-upload (https://github.com/danialfarid/ng-file-upload) on the frontend to manage the file upload process.
Unfortunately, form contains a complex object with multiple files. Using the MultipartFormData (https://www.playframework.com/documentation/2.5.x/ScalaBodyParsers) on the server side I have successfully decomposed the uploaded content and can read it from the request.body.
Now, to my surprise, I do not have a simple Json Objects but rather a strangely formed datatype, described on the ng-file-upload website as:
(...) server implementations expecting nested data object keys in .key or [key] format.
Example: data: {rec: {name: 'N', pic: file}} sent as: rec[name] -> N, rec[pic] -> file
data: {rec: {name: 'N', pic: file}, objectKey: '.k'} sent as: rec.name -> N, rec.pic -> file
So far I have managed to bring all the data to a common MultipartFormData.Part type, using the DataPart and FilePart like this:
val opts = body.dataParts.map {
case (key, values) => DataPart(key, values.head)
}
val parts = opts ++ body.files
So I am now left with a quite unfortunate Iterable[Part]:
0 = {MultipartFormData$DataPart#86271} "DataPart(arabic[active],false)"
1 = {MultipartFormData$DataPart#86273} "DataPart(english[active],true)"
2 = {MultipartFormData$DataPart#86277} "DataPart(english[url],2132132132)"
...
7 = {MultipartFormData$FilePart#76473} "FilePart(english[image],fb_icon_325x325.png,Some(image/png),TemporaryFile(/tmp/playtemp5909927824995768544/multipartBody8348573128070542611asTemporaryFile))"
Each object name contains the key of it's Json structure and its according value. Now instead of key[level1][level2] I would like to parse it to objects, in my case:
case class PcBanner(english: PcBanners, arabic: PcBanners, kurdish: PcBanners)
case class PcBanners(active: Boolean, url: Option[String], image: Option[String])`
I hope you got the idea.
The question
I know I could try to parse the name strings trying to fit it to objects, but I believe I made a mistake someway in the middle.
Is there a way to parse this structure into the objects, using field names as a reference? Any build in Play functions or alike?
Thanks for help!
As I stated in the title my case was to send images. As you would expect, I am also presenting a preview and the files currently saved in the database.
Considering all pros and cons I have decided to send all the data in JSON format, both ways. Meaning that the images are encoded and sent along in JSON structure.
Despite the fact that above solution looks very convenient it actually creates new problems during the implementation.
You will quickly exceed the server's POST request size limit. For Play server the default 100kB is possible to be extended, but...
I have soon run into some data malformations as the image saved as huge String of bytes probably had some sending/parsing errors.
Not going deeper into this faulty solution I have used the #danial advice:
No have the file sent separately like this
{file: file, otherData: JSON.stringify(myData)}
My solution
If anyone would like to use similar approach to mine I present my answer.
On the front-end side I have decided used ng-file-upload library. Binding it to HTML component with ngf-select with ngf-drop which enables the component:
<div ngf-drop ngf-select
ng-model="image"
ngf-accept="'image/*'"
ngf-resize="{width: {{width}}, height: {{height}}, quality: 1.0, restoreExif: false}">
<img ng-show="!!image && !!image.$ngfName" ngf-src="image">
<img ng-show="(!image || !image.$ngfName)" ng-src="{{ imageUrl }}">
</div>
Inside the upload tag I put the image preview. This works flawlessly. If the image is not selected I use the image saved in the db.
The data and images do not share the model anymore. The upload function looks as follow:
return Upload.upload({
url: url,
data: {file: images, data: angular.toJson(data)}
}).then(function (resp) {
console.log(resp);
}, function (error) {
console.log(error);
});
Putting together all the above gave me the output data object:
{
"english":{
"active":true,
"url":"http://google.com"
},
"arabic":{
"active":true,
"url":"http://google.com"
},
"kurdish":{
"active":true,
"url":"http://google.com"
}
}
On the server side the JSON matches the prepared case class and is parsed with build-in Jackson parser, allowing for easy object manipulation. The image has to be manually selected:
val json = r.body.dataParts("data")
val jsValue = Json.parse(json.head)
val result = jsValue.validate(LocalizedBanner.dataModelFormat) // parse JSON
Extracting the files from body can be done with build in function .file:
val key = s"file[${lang.name}][${imageType.name}]"
body.file(key).map(mp => (mp.ref.file, imageType))
Enjoy!

AngularFire - How do I query denormalised data?

Ok Im starting out fresh with Firebase. I've read this: https://www.firebase.com/docs/data-structure.html and I've read this: https://www.firebase.com/blog/2013-04-12-denormalizing-is-normal.html
So I'm suitably confused as one seems to contradict the other. You can structure your data hierarchically, but if you want it to be scalable then don't. However that's not the actual problem.
I have the following structure (please correct me if this is wrong) for a blog engine:
"authors" : {
"-JHvwkE8jHuhevZYrj3O" : {
"userUid" : "simplelogin:7",
"email" : "myemail#domain.com"
}
},
"posts" : {
"-JHvwkJ3ZOZAnTenIQFy" : {
"state" : "draft",
"body" : "This is my first post",
"title" : "My first blog",
"authorId" : "-JHvwkE8jHuhevZYrj3O"
}
}
A list of authors and a list of posts. First of all I want to get the Author where the userUid equals my current user's uid. Then I want to get the posts where the authorId is the one provided to the query.
But I have no idea how to do this. Any help would be appreciated! I'm using AngularFire if that makes a difference.
Firebase is a NoSQL data store. It's a JSON hierarchy and does not have SQL queries in the traditional sense (these aren't really compatible with lightning-fast real-time ops; they tend to be slow and expensive). There are plans for some map reduce style functionality (merged views and tools to assist with this) but your primary weapon at present is proper data structure.
First of all, let's tackle the tree hierarchy vs denormalized data. Here's a few things you should denormalize:
lists you want to be able to iterate quickly (a list of user names without having to download every message that user ever wrote or all the other meta info about a user)
large data sets that you view portions of, such as a list of rooms/groups a user belongs to (you should be able to fetch the list of rooms for a given user without downloading all groups/rooms in the system, so put the index one place, the master room data somewhere else)
anything with more than 1,000 records (keep it lean for speed)
children under a path that contain 1..n (i.e. possibly infinite) records (example chat messages from the chat room meta data, that way you can fetch info about the chat room without grabbing all messages)
Here's a few things it may not make sense to denormalize:
data you always fetch en toto and never iterate (if you always use .child(...).on('value', ...) to fetch some record and you display everything in that record, never referring to the parent list, there's no reason to optimize for iterability)
lists shorter than a hundred or so records that you always as a whole (e.g. the list of groups a user belongs to might always be fetched with that user and would average 5-10 items; probably no reason to keep it split apart)
Fetching the author is as simple as just adding the id to the URL:
var userId = 123;
new Firebase('https://INSTANCE.firebaseio.com/users/'+userId);
To fetch a list of posts belonging to a certain user, either maintain an index of that users' posts:
/posts/$post_id/...
/my_posts/$user_id/$post_id/true
var fb = new Firebase('https://INSTANCE.firebaseio.com');
fb.child('/my_posts/'+userId).on('child_added', function(indexSnap) {
fb.child('posts/'+indexSnap.name()).once('value', function(dataSnap) {
console.log('fetched post', indexSnap.name(), dataSnap.val());
});
});
A tool like Firebase.util can assist with normalizing data that has been split for storage until Firebase's views and advanced querying utils are released:
/posts/$post_id/...
/my_posts/$user_id/$post_id/true
var fb = new Firebase('https://INSTANCE.firebaseio.com');
var ref = Firebase.util.intersection( fb.child('my_posts/'+userId), fb.child('posts') );
ref.on('child_added', function(snap) {
console.log('fetched post', snap.name(), snap.val();
});
Or simply store the posts by user id (depending on your use case for how that data is fetched later):
/posts/$user_id/$post_id/...
new Firebase('https://INSTANCE.firebaseio.com/posts/'+userId).on('child_added', function(snap) {
console.log('fetched post', snap.name(), snap.val());
});

Get meta data from delegated files using QML

I'm creating a music player for Ubuntu Touch in QML and I have some things I would appreciate some help with since I'm new to QML.
I have a list of tracks from a directory, but I want to show the meta data (artist, track name, year, album and so on) instead of the filename.
Using Qt.Multimedia am able to get the meta data from the currently playing track, but I can't find how to do it per file from my FolderListModel delegated files.
How would I do that?
This is the current code:
Column {
anchors.centerIn: parent
anchors.fill: parent
ListView {
id: musicFolder
FolderListModel {
id: folderModel
folder: musicDir
showDirs: false
nameFilters: ["*.ogg","*.mp3","*.oga","*.wav"]
}
width: parent.width
height: parent.height
model: folderModel
delegate: ListItem.Subtitled {
text: fileName
subText: "Artist: "
onClicked: {
console.debug('Debug: User pressed '+musicDir+fileName)
playMusic.source = musicDir+fileName
playMusic.play()
trackInfo.text = playMusic.metaData.albumArtist+" - "+playMusic.metaData.title // show track meta data
}
}
}
}
It seems like the easiest thing to do here would be to go get a C++ library that can parse the meta data out of these files and use it to create a custom ListModel in C++ that populates this information onto itself. Unfortunately, this will have to be done in C++ as javascript does not have the IO capabilities to read and parse files.
Actually I think I might go ahead and use QtMultimedia, but keep data in a local database. On startup, it checks the music dir and adds/removes tracks. That way it will be only be slow at first startup (hopefully)

How to set up CakePHP 2.x to behave like a RESTful webservices (for using it together with JavascriptMVC)

I am trying to set up cakephp to work with the very nice javascriptMVC (http://forum.javascriptmvc.com). JavaScriptMVC requires the JSON-Output in the following format:
[{
'id': 1,
'name' : 'Justin Meyer',
'birthday': '1982-10-20'
},
{
'id': 2,
'name' : 'Brian Moschel',
'birthday': '1983-11-10'
}]
Cake would generate a deeper nested array with a prepended Class Name. I found attempts to solve the problem but theyre not for cakephp 2.x. I know that I can simply generate a new array and json_encode() it via php, but it would be nicer to include a function like this https://gist.github.com/1874366 and another one to deflatten it.
Where would be the best place to put such functions? The AppController doesnt seem to work. Should i put it in beforeRender () or beforeFilter() of the controller? Or does someone maybe even know of an existing solution/plugin for this? This would be the best for me in my current Situation, as Im pretty much pressed for time.
Ok, I'm not 100% sure I understand what you are trying to do so here's a word to the wise just in case: Cake and JMVC are both comprehensive MVC frameworks. if you are attempting to combine them as a single cohesive platform to build your application, I strongly suggest you review your approach / platform / etc.
Also -- I'm not an expert by any means in jmvc, so I'm just going to pretend that processing the response from Cake in jmvc is completely out of the question, for some odd reason. For the record, think of Cake's responses like this:
{ "Model" :
[{
'id': 1,
'name' : 'Justin Meyer',
'birthday': '1982-10-20'
},
{
'id': 2,
'name' : 'Brian Moschel',
'birthday': '1983-11-10'
}]
}
Cake has had comprehensive REST service support, since at least Cake 1.2. The lib you are interested in is HttpSocket. As for json encoding and serving response, Request Handling covers, among other things, responding to all manners of requests, content types, decoding and encoding json, etc. Finally, the built-in Set utility will almost certainly cover whatever array manipulation you need in a line or two.
The functionality you are interested in is pretty basic and hasn't changed too much. I'd bet a lot of the (reasonably simple) solutions you have already found would probably still work, maybe with a little bit of tweaking.
For pretty much any basic service endpoint, you would probably create a controller (not AppController - that is application-wide, hence you can't invoke it directly) method, considering Cake routes the controller/action into your url:
Cake consuming services from a different app would look like this:
http://cakeproject/collect/getInfo
class CollectController extends AppController {
public function getInfo($array = null) {
App::uses('HttpSocket', 'Network/Http');
$http = new HttpSocket();
$http->get('http://jmvcproject/controller/action', $array);
// ...etc.
}
Cake providing services from the same controller / action to a different app would simply be:
public function getInfo($array = null) {
$results = $this->Collect->find('all', $array);
// ...fetch the results
}
Or you could just loop over that array with foreach($this->data as $data) { ... to drop the class name. But if your data will include associated models, etc, Set is probably the most versatile and resilient solution.
Anyway, HTH

How to change fusion table maps markers' size?

I have a table with a location column and "count" column (with values from 1 to 100).
I'd like to map the records with markers that change in size, i.e. the bigger the count value is, the bigger the marker is.
Is that possible in Google Fusion? How would you suggest to do that?
Thanks.
Currently there are only 2 sizes of icons available: small and large, I put together a little example to show you how to use them together with the FusionTablesLayer, which is a special layer for Google Maps that can use to query your Google Fusion Tables.
FusionTablesLayer allow you apply a style to your data (markers, lines or polygons), it boils down to this:
layer = new google.maps.FusionTablesLayer({
query: {
select: 'Location',
from: '3609183'
},
styles: [
{ where: "Number > 1000",
markerOptions: {
iconName: 'large_green'
}
},
{ where: "Number <= 1000",
markerOptions: {
iconName: 'large_red'
}
},
{ where: "Number <= 100",
markerOptions: {
iconName: 'small_purple'
}
}
]});
If two sizes are not enough, then maybe you can play around with different colors/icons (there is a list with supported icons). Otherwise you have to retrieve your data and create custom markers with images of different size.
Javram pointed to one approach, but the list of available marker icons is limited in Fusion Tables and AFAIK there is no way to vary the icon size. Another approach might be to use the JSONP support provided by Fusion Tables to retrieve you your data and create your own makers. This blog post explains how to do it.
The answer is here, http://support.google.com/fusiontables/bin/answer.py?hl=en&answer=185991 basically, you need to add a column in your table that is the name of the marker type you want to use for that location.

Resources