Using Imagemagick with a file from a mongo collection - meteor

I'm building a simple database for small pixel-art files. The images are saved directly to the database:
Template.pixUpload.events({
'change .myPixInput': function(event, template) {
event.preventDefault();
var file = event.target.files[0]; //assuming 1 file only
if (!file) return;
var reader = new FileReader();
reader.onload = function(event){
MyPix.insert({
binary: reader.result,
createdAt: new Date
});
}
reader.readAsDataURL(file);
}
})
The idea is to be able to modify the images on their way back to the browser, scale them on the fly (if things don't get too slow). So I'm trying to read the image from the db, and scale it with Imagemagick, before displaying it. It doesn't work – and I can't find anything helpful I would be able to understand:
Template.pixList.helpers({
'thumbnail': function() {
var bin = this.binary;
var thumb = new FileReader();
Imagemagick.convert(['bin', '-filter', 'point', '64x64', 'thumb']);
return thumb;
}
})

im using GM Package Right now, take a look at full repo here
First Install All FSCollecton Packages.
GridFS (Because you say you want to store the file inside MongodB).
Graphicsmagick meteor add cvs:graphicsmagick
Second Declare collections on /lib/collection.js
kaiStackExample = new FS.Collection("kaiStackExample ", {
stores: [new FS.Store.GridFS("kaiStackExample ",{
beforeWrite:function(fileObj){
return {
extension: 'png',
type: 'image/png'
};
},
transformWrite:function(fileObj, readStream, writeStream){
// Here you can choose, for example 10x10 (thumbnail), etc.
gm(readStream).resize(600).stream('PNG').pipe(writeStream);
}
})]
});
and our basic Subscribe, on the same /lib/collection.js
if(Meteor.isClient) {
Meteor.subscribe('kaiStackExample');
}
Ok, at this point we have the GridFS and GM, to verify both
server console.
=> GraphicsMagick found
Client console.
kaiStackExample.find().fetch();
should return [];
Third SECURITY
kaiStackExample.allow({
insert:function(userId,doc){
//here we can do stuff like, only permit user with accounts insert on the collection
if(!Meteor.userId()){
return false;
}else{
return true
},
update:function(userId,doc){
if(userId === doc.authorId{
return true;
}else{
return false; // if user don't own the document can update it.
}
}
});
Four Template and Events
HTML markup
<template name="exampleKai">
Select File
<input type="file" id="fileExampleKai">
<button type="submit" id="buttonExampleKai">Click to upload</button>
</template>
Js Code
Template.exampleKai.events({
'click #buttonExampleKai':function(event,template){
event.preventDefault();
var file = $('#fileExampleKai').get(0).files[0];
fsFile = new FS.File(file);
fsFile.metadata = {
coolMetadata:"Yes You can add some metadata too"
}
if(file === undefined){
alert("Please upload a file to continue")
}else{
kaiStackExample.insert(fsFile,function(err,succes){
if(err){
console.log(err.reason);
}else{
console.log("ok we insert yeaaa")
}
});
}
}
});
Like i say this works for me, and i think its your best option for editing size,type,etc Take a look hope it help you

Even though Meteor is very good at keeping the client and server environments in sync, that doesn't mean it can do everything on the client that it can do on the server and vice versa. Converting images using ImageMagick would be one example of what you can only do on the server.
If I were to build something like this, I'd look into using CollectionFS for syncing files. They also have a section in the README that describes how to manipulate images before saving them, which seems to be just what you're after.

Related

Ionic and SQLite wait until database info is loaded

I have a project on Ionic where I need to render some information of the database in the home page, something like a TODO program.
I already have some information on the database and I'm trying to render the list of items but I have the next problem:
First the home page is loaded without any result
Then the data from the database is loaded and printed on the screen
The problem is I want the view to wait until the database info is loaded until showing anything, I'm wondering if I can use some kind of loading icon.
I've followed the answer here: Open database before main controller is called in Ionic and SQlite
I have the database initialization working but as I've said, the data is loaded after the view is rendered.
I've tried using $ionicLoading but I didn't get any good result
This is my view:
.controller('homeCtrl', function ($scope, $state, $cordovaSQLite, DB) {
$scope.$on('$ionicView.enter', function() {
tasks = []
var query = "SELECT * FROM task;";
$cordovaSQLite.execute(DB.db, query, []).then(function(results) {
if(results.rows.length > 0) {
for (i=0; i<results.rows.length; i++){
console.log("SELECTED -> " + results.rows.item(0).title);
$scope.tasks.push(results.rows.item(i))
}
} else {
console.log("No results found");
}
}, function (err) {
$scope.tasks = [];
console.error(err);
});
$scope.tasks = tasks;
});
})
This is a video example of the issue I'm having right now:
https://youtu.be/H2fUYQuV3xg
Finally I found a solution following the advice of using resolve in my routes.
.state('home', {
url: '/',
templateUrl: 'templates/home.html',
controller: 'homeCtrl',
resolve: {
tasks: function(DB) {
return DB.getTasks();
});
}
}
})
I have a factory called DB where I have some functions to retrieve data from the database. On this example I load the tasks before entering on the URL using DB.getTasks()
To load the variable tasks resolved on the route I have to add the variable name on the function like this:
app.controller('homeCtrl', function (tasks) {
$scope.tasks = tasks;
})

Meteor reading data from a file stored on server

I want to show some data points on the client side, and I want to get the latitude, longitude and summary from a file stored on the server.
I have read a lot of posts saying to use papaParse using Meteor methods but I am not able to make it work.
Can you guys point me to right direction, my questions are:
In which folder to should I store a .txt, .csv or .json file in Meteor?
How to access it from the client and return the read data to client for display.
You can put your static files into private folder on the server and get them through Assets.
For exmaple, you have a data.json file in your private folder.
Method to get this data:
Meteor.methods({
getData() {
return JSON.parse(Assets.getText('data.json'));
}
});
You can now call this method on the client:
Meteor.call('getData', function(err, res) {
console.log(res);
});
UPD
Ok, how to display it.
Meteor.call runs async, so we will use reactivity to update our view on result.
Here is how we can display data on ourData template.
<template name="ourData">
<!-- Here you may want to use #each or whatever -->
<p>{{ourData}}</p>
</template>
Template.ourData.onCreated(function() {
this.ourData = new ReactiveVar();
Meteor.call('getData', (err, res) => {
if (err) {
console.error(err);
} else {
// Putting data in reactive var
this.ourData.set(res);
}
});
});
Template.ourData.helpers({
ourData: function() {
// Helper will automatically rerun on method res
return Template.instance().ourData.get();
}
});
reactive-var package required or you can also use Session.

Updating client view in MeteorJS application

I'm new to MeteorJS and there is something that I'm not understanding with subscriptions and publications. I have a tablets collections. This is my setup with autopublish removed.
Server/publictions.js
Meteor.publish('tablets', function() {
return Tablets.find({}, {sort: {manufacturer: 1}});
});
In Client/view.js
Meteor.subscribe('tablets');
Template.tabletsList.helpers({
tablets: function() {
return Tablets.find();
}
});
Then in client/view.html
{{#each tablets}}
{{> tabletPreview}}
{{/each}}
This all works fine and I can see my tablets. But now I have a search box and when the search is submitted I want to update the tablets view to only show the search results.
I have a events handler but can't figure out how to update the tablets to only show the search results as the below code doesn't work. Should I use a session instead or have a totally new view.
client.view.js
Template.tabletsList.events({
"click .search": function (event, template) {
var query = $('input[name=search]').val();
Template.tabletsList.tablets = Tablets.find({manufacturer: query}, {sort: {manufacturer: 1}});
}
});
Here's the approach:
Add a reactive variable to your template on creation which holds the user's last search.
When the user updates the search, you should update the reactive variable.
Use the reactive variable in your helper to fetch only the matching documents.
Here is one possible implementation:
Template.tabletsList.created = function() {
// add a reactive variable to this template to track the search
this.search = new ReactiveVar('');
};
Template.tabletsList.events({
'click .search': function (event, template) {
var query = $('input[name=search]').val();
// update the search based on the user's input
template.search.set(query);
}
});
Template.tabletsList.helpers({
tablets: function() {
// read the user's last search (if any)
var search = Template.instance().search.get();
// sort options
var options = {sort: {manufacturer: 1}};
if (search.length) {
// find all tablets matching the search expression
var re = new RegExp(search);
return Tablets.find({manufacturer: re}, options);
}
else {
// if the user didn't input a search just find all tablets
return Tablets.find({}, options);
}
}
});
Notes:
See this post for more details on scoped reactivity.
Sorting in a publish doesn't really do anything for you in most cases. See my article on common mistakes.

Can I publish only Collections object in meteor?

On the introducing article of DDP, I read that anything can be published, but I've read somewhere ( for example in this Stackoverflow comment Publish arbitrary data and automatically update HTML ) that only Collections can be published.
So where is the truth? If we can publish other things than Collections, I would liek to see an example as I can't find one so far.
From the docs: http://docs.meteor.com/#meteor_publish
Publish functions can return a Collection.Cursor, in which case Meteor will publish that cursor's documents. You can also return an array of Collection.Cursors, in which case Meteor will publish all of the cursors.
So at the moment you can only return a Collection via a cursor (result of a Collection.find()).
To return other data you need to hack into the sockjs stream (the socket library meteor uses to communicate to the server). Be aware this does not guarantee compatibility with future versions of meteor. Sockjs is the library used for meteor to communicate between the server (the wire)
from Publish arbitrary data and automatically update HTML*
client side js
sc = new Meteor._Stream('/sockjs');
sc.on('message', function(payload) {
var msg = JSON.parse(payload);
Session.set('a_random_message', JSON.stringify(msg.data));
});
Template.hello.greeting = function () {
return Session.get('a_random_message');
};
server side js
ss = new Meteor._StreamServer();
ss.register(function (socket) {
var data = {socket: socket.id, connected: new Date()}
var msg = {msg: 'data', data: data};
// Send message to all sockets (which will be set in the Session a_random_message of the client
_.each(ss.all_sockets(), function(socket) {
socket.send(JSON.stringify(msg));
});
});
You can also look at Meteor Streams too. See below.
assume you have added meteor streams via atmosphere - mrt add streams
sc = new Meteor.Stream('hello');
if(Meteor.isServer) {
Meteor.setInterval(function() {
sc.emit('a_random_message', 'Random Message: ' + Random.id());
}, 2000);
Meteor.permissions.read(function() { return true });
}
if(Meteor.isClient) {
sc.on('a_random_message', function(message) {
Session.set('a_random_message', message);
});
Template.hello.greeting = function () {
return Session.get('a_random_message');
};
}

dynamically add files to the public folder and getting the refreshed results in meteor

I'm playing around with the idea of using Meteor as a backend for creating a static html generator (with an admin UI).
I want that, when triggered, a new file will be created on the public folder and instantly reviewed by the author on an iframe embed in the html.
The file gets created but two side effects happen:
The server is restarted.
The file is cached - so the user can't see that the change took place.
Any ideas?
if (Meteor.is_client) {
Template.hello.events = {
'click input' : function () {
Meteor.call('makeFile', 'filename.html', function(error, result){
alert(result);
});
//window.location = '/filename.txt';
// template data, if any, is available in 'this'
if (typeof console !== 'undefined')
console.log("You pressed the button");
}
};
}
if (Meteor.is_server) {
var fs = __meteor_bootstrap__.require('fs');
Meteor.startup(function () {
// code to run on server at startup
});
Meteor.methods({
'makeFile': function(fileName) {
/*
fs.unlink("public/"+fileName, function (err) {
if (err) throw err;
console.log('successfully deleted ');
});
*/
fs.writeFile("public/"+fileName, "<html><body><h1>test</h1></body></html>", function(err) {
if(err) {
console.log(err);
} else {
console.log("The file was saved! "+ fileName);
}
});
return fileName;
}
});
}
I think the meteor server restarts whenever you change any file in a meteor subdirectory. So to prevent this don't write your files within the meteor app directory, make a created_files directory one level up or something. This is a good idea anyway, to separate the generated files from the code that's generating them.
Easiest way to show the content in the admin interface is probably to write it to the database (as opposed to trying to watch for directory changes). No sense re-inventing the wheel, since Meteor is designed to watch for db updates.
Create a collection (at the top, so it's on client & server):
GeneratedFiles = new Meteor.Collection("generated_files");
Then assign the file contents to a variable before writing them and in your fs.writeFile callback:
if(err) {
console.log(err);
} else {
GeneratedFiles.insert({name: fileName, contents: fileContents});
console.log("The file was saved! "+ fileName);
}
And then auto-publish the file contents to template in the client code (not sure why you want to use an iframe, seems like a div would work fine. But either way would probably work).
Hope that helps. And good luck, I think meteor is a great framework for this kind of thing. Most of the admin interfaces I use are still entirely synchronous and just annoying to work with.

Resources