How can one remove a whole IndexedDB database from JavaScript, as opposed to just an object store? I'm using the IndexedDB shim, which may use WebSQL as its backend.
I'd mainly like to know how to do this for the PhantomJS (headless) browser, although Chrome, Safari (on iPad) and IE10 are other important browsers.
As far as I can tell, one should use indexedDB.deleteDatabase:
var req = indexedDB.deleteDatabase(databaseName);
req.onsuccess = function () {
console.log("Deleted database successfully");
};
req.onerror = function () {
console.log("Couldn't delete database");
};
req.onblocked = function () {
console.log("Couldn't delete database due to the operation being blocked");
};
I can confirm that it works with PhantomJS 1.9.0 and Chrome 26.0.1410.43.
I found that the following code works OK but to see the DB removed in the Chrome Resources Tab I have had to refresh the page.
Also I found I had problems with the Chrome debug tools running while doing transactions. Makes it harder to debug but if you close it while running code the code seems to work OK.
Significant also is to set a reference to the object store when opening the page.
Obviously the delete part of the code is in the deleteTheDB method.
Code derived from example provided by Craig Shoemaker on Pluralsight.
var IndDb = {
name: 'SiteVisitInsp',
version: 1000,
instance: {},
storenames: {
inspRecords: 'inspRecords',
images: 'images'
},
defaultErrorHandler: function (e) {
WriteOutText("Error found : " + e);
},
setDefaultErrorHandler: function (request) {
if ('onerror' in request) {
request.onerror = db.defaultErrorHandler;
}
if ('onblocked' in request) {
request.onblocked = db.defaultErrorHandler;
}
}
};
var dt = new Date();
var oneInspRecord =
{
recordId: 0,
dateCreated: dt,
dateOfInsp: dt,
weatherId: 0,
timeArrived: '',
timeDeparted: '',
projectId: 0,
contractorName: '',
DIWConsultant: '',
SiteForeman: '',
NoOfStaffOnSite: 0,
FileME: '',
ObservationNotes: '',
DiscussionNotes: '',
MachineryEquipment: '',
Materials: ''
};
var oneImage =
{
recordId: '',
imgSequence: 0,
imageStr: '',
dateCreated: dt
}
var SVInsp = {
nameOfDBStore: function () { alert("Indexed DB Store name : " + IndDb.name); },
createDB: function () {
openRequest = window.indexedDB.open(IndDb.name, IndDb.version);
openRequest.onupgradeneeded = function (e) {
var newVersion = e.target.result;
if (!newVersion.objectStoreNames.contains(IndDb.storenames.inspRecords)) {
newVersion.createObjectStore(IndDb.storenames.inspRecords,
{
autoIncrement: true
});
}
if (!newVersion.objectStoreNames.contains(IndDb.storenames.images)) {
newVersion.createObjectStore(IndDb.storenames.images,
{
autoIncrement: true
});
}
};
openRequest.onerror = openRequest.onblocked = 'Error'; //resultText;
openRequest.onsuccess = function (e) {
//WriteOutText("Database open");
IndDb.instance = e.target.result;
};
},
deleteTheDB: function () {
if (typeof IndDb.instance !== 'undefined') {
//WriteOutText("Closing the DB");
IndDb.instance.close();
var deleteRequest = indexedDB.deleteDatabase(IndDb.name)
deleteRequest.onblocked = function () {
console.log("Delete blocked.");
}
deleteRequest.onerror =
function () {
console.log("Error deleting the DB");
//alert("Error deleting the DB");
};
//"Error deleting the DB";
deleteRequest.onsuccess = function () {
console.log("Deleted OK.");
alert("*** NOTE : Requires page refresh to see the DB removed from the Resources IndexedDB tab in Chrome.");
//WriteOutText("Database deleted.");
};
};
}
}
Related
I am on Chrome Version 57.0.2987.110 (64-bit) on MacOS/OSX 12.3 Sierra,
I managed to get a service worker working, but I must say I am new at this.
I now try to get it to use an IndexedDB and store data in it.
It fetches data from a local web server in http and retrieve it without problem in json format.
The data format is :
[{"id":"1", "...":"...", ...},
{"id":"2", "...":"...", ...},
{"id":"3", "...":"...", ...},
...
{"id":"n", "...":"...", ...}]
it then adds the data to an IndexedDB without problem apparently because the onsuccess callback is triggered ...
entityObjectStore.add success : Event {isTrusted: true, type: "success", target: IDBRequest, currentTarget: IDBRequest, eventPhase: 2…}
But it does not appear in the Chrome dev tools!
Here is my service worker:
self.addEventListener('install',function(event)
{
event.waitUntil(
(new Promise(function(resolve,reject)resolve()}))
.then(function()
{return self.skipWaiting();}));
});
self.addEventListener('activate', function(event)
{
event.waitUntil(
(new Promise(function(resolve,reject){resolve()}))
.then(function() {return self.clients.claim();}));
});
self.addEventListener('message', function(event)
{
if(event.data.command=="data.load")
{
var options = event.data.options
var url = new URL(event.data.host+options.source);
var parameters = {entity:options.name,
options:encodeURIComponent(options.options)};
Object.keys(parameters)
.forEach(key => url.searchParams.append(key,
parameters[key]))
fetch(url).then(function(response)
{
if(response.ok)
{
response.json().then(function(data_json)
{
var entityData = data_json;
var request = indexedDB.open(options.name);
request.onerror = function(event)
{
console.log("indexedDB.open error :",event);
};
request.onsuccess = function(event)
{
console.log("indexedDB.open success :",event)
var db = event.target.result;
}
request.onupgradeneeded = function(event)
{
console.log("indexedDB.open upgrade :",event)
var db = event.target.result;
db.createObjectStore(options.options.id, { keyPath: "id" });
objectStore.transaction.oncomplete = function(event)
{
var entityObjectStore = db.transaction(options.options.id, "readwrite").objectStore(options.options.id);
for (var i in entityData)
{
var addRequest = entityObjectStore.add(entityData[i]);
addRequest.onerror = function(event)
{
console.log("entityObjectStore.add error :",event);
};
addRequest.onsuccess = function(event)
{
console.log("entityObjectStore.add success :",event);
};
}
}
};
});
}
});
}
else if(event.data.command=="data.get")
{
var command = event.data;
var request = indexedDB.open(command.domain);
request.onerror = function(event)
{
console.log("indexedDB.open error :",event);
};
request.onsuccess = function(event)
{
console.log("indexedDB.open success :", event)
var db = event.target.result;
var transaction = db.transaction([command.domain]);
var objectStore = transaction.objectStore(command.entity);
var request = objectStore.get(command.id);
request.onerror = function(event)
{
console.log("objectStore.get error :",event);
};
request.onsuccess = function(event)
{
console.log("objectStore.get success :",event);
console.log("request.result=" + request.result);
};
}
}
});
Here is the HTML file I use with it :
<!DOCTYPE html>
<html>
<head>
<title>My service worker app</title>
</head>
<body>
<button id="update">Update</button>
<div id="log"></div>
<script type="text/javascript">
function sendMessage(message)
{
return new Promise(function(resolve, reject)
{
var messageChannel = new MessageChannel();
messageChannel.port1.onmessage = function(event)
{
if (event.data.error)
{
reject(event.data.error);
}
else
{
resolve(event.data);
}
};
});
}
if (navigator.serviceWorker.controller)
{
var url = navigator.serviceWorker.controller.scriptURL;
var initData = [
{
name:"my_entity_type",
source:"/webapp/data",
options:{id:"my_entity_type_id"}
}
]
for(var dataIndex in initData)
{
var data = initData[dataIndex]
sendMessage({
command:"data.load",
host: document.location.origin,
options: data
});
}
}
else
{
navigator.serviceWorker.register('/webapp/lt_sw.js')
.then(function(registration)
{
debug('Refresh to allow ServiceWorker to control this client', 'onload');
debug(registration.scope, 'register');
});
}
navigator.serviceWorker.addEventListener('controllerchange',
function()
{
var scriptURL = navigator.serviceWorker.controller.scriptURL;
debug(scriptURL, 'oncontrollerchange');
});
document.querySelector('#update').addEventListener('click',
function()
{
navigator.serviceWorker.ready.then(function(registration)
{
registration.update()
.then(function()
{
console.log('Checked for update');
})
.catch(function(error)
{
console.error('Update failed', error);
});
});
});
function debug(message, element)
{
var target = document.querySelector('#log');
console.log(element,":",message)
target.textContent = message;
}
</script>
</body>
</html>
Any idea about what I am, doing wrong?
Edit 1:
I modified the script in the html file so that update mechanism sends another message to the service worker asking for data :
document.querySelector('#update').addEventListener('click',
function()
{
navigator.serviceWorker.ready.then(function(registration)
{
registration.update()
.then(function()
{
console.log('Checked for update');
sendMessage({
command:"data.get",
domain:"database_name",
entity: "my_entity_type",
id: "my_entity_id"
});
})
.catch(function(error)
{
console.error('Update failed', error);
});
});
});
And I added the following line:
indexedDB.webkitGetDatabaseNames().onsuccess = function(sender,args){ console.log("data.get all dbs:",sender.target.result); };
Just at the beginning of the data look up part:
else if(event.data.command=="data.get")
{
To list all the IndexedDBs on Chrome as explained here : https://stackoverflow.com/a/15234833/2360577
And I get all the databases I created previously!
data.get all dbs: DOMStringList {0: "db1", 1: "db2", 2: "db3", 3: "db4", length: 4}
Then I proceeded to list all ObjectStores in these dbs, also as explained in the previous link, by adding the following line :
indexedDB.open(<databaseName>).onsuccess = function(sender,args)
{"<databaseName>'s object stores",console.log(sender.target.result.objectStoreNames); };
And apart for one, where the data is processed and apparently does not work, they all had one object store with the same name as the db that contains it as expected ...
db1's object stores DOMStringList {0: "db1", length: 1}
db2's object stores DOMStringList {length: 0}
db3's object stores DOMStringList {0: "db3", length: 1}
db4's object stores DOMStringList {0: "db4", length: 1}
Then I had a look inside the object stores ...
indexedDB.open("db3").onsuccess = function(event)
{event.target.result.transaction(["db3"]).objectStore("db3")
.getAll().onsuccess = function(event)
{console.log("objectStore.getAll() success :",event.target.result)};};
And the entries are there as expected! But not in Application/Storage ...
Edit 2:
The error for db2 was apparently that I did not refresh the data in the database ... this part works now ...
Edit 3:
The answer was as simple as closing and reopening the dev tools ... like #wOxxOm suggested ...
It works for me if I close DevTools and open again. Did you try that?
You could also check the "Update on reload" button and try hard reset ctrl/shift+f5 to the same effect as #wOxxOm mentioned.
I'm working with NativeStorage and barcodeScanner plugins for cordova.
The capture works well, and I receive the QRCode, but for any reason angular doesn't print it.
After working a lot on my code, I'm not able to do a valid callback, so angular can print it binding the data.
Here bellow I paste the code.
read.js
(function() {
'use strict';
var read = angular.module('app.read', ['monospaced.qrcode']);
read.controller('ReadController', [
function() {
var data = this;
var qr = function(string) {
data.code = string;
console.log(string);
};
cordova.plugins.barcodeScanner.scan(
function(result) {
if (!result.cancelled) {
if (result.format === "QR_CODE") {
(function(cb) {
cb(result.text);
})(qr);
NativeStorage.getItem("historic", function(d) {
var storage = JSON.parse(d);
storage.push(result.text);
NativeStorage.setItem("historic", JSON.stringify(storage), function(response) {}, function(e) {
console.log(e);
});
}, function(e) {
window.alert("Scanning failed: " + e);
});
}
}
},
function(e) {
window.alert("Scanning failed: " + e);
}, {
"preferFrontCamera": true, // iOS and Android
"showFlipCameraButton": true, // iOS and Android
"prompt": "Place a barcode inside the scan area", // supported on Android only
"formats": "QR_CODE,PDF_417", // default: all but PDF_417 and RSS_EXPANDED
"orientation": "portrait" // Android only (portrait|landscape), default unset so it rotates with the device
}
);
}
]);
}());
read.html
<div ng-controller="ReadController as myRead">
<qrcode version="5" error-correction-level="H" size="200" data="{{myRead.code}}" href="{{myRead.code}}"></qrcode>
{{myRead.code}}
</div>
Just adding some extra tests I have done before, I just missed the barcodeScanner.scan process and I did just the storage as I show bellow:
NativeStorage.getItem("historic", function (d) {
var storage = JSON.parse(d);
storage.push('https://google.es');
data.code = 'https://google.es';
NativeStorage.setItem("historic", JSON.stringify(storage), function (response) {}, function (e) {
console.log(e);
});
}, function (e) {
window.alert("Scanning failed: " + e);
});
Could you show me where am I wrong?
Thanks in advice.
A qualified guess is that the callbacks from cordova.plugins.barcodeScanner.scan doesn't trigger AngularJS' digest cycle, which means no dirty checking will be performed, no changes will be detected and the UI won't be updated.
Try wrapping the code in the success callback in $apply:
function(result) {
$scope.$apply(function() {
if (!result.cancelled) {
if (result.format === "QR_CODE") {
(function(cb) {
cb(result.text);
})(qr);
NativeStorage.getItem("historic", function(d) {
var storage = JSON.parse(d);
storage.push(result.text);
NativeStorage.setItem("historic", JSON.stringify(storage), function(response) {}, function(e) {
console.log(e);
});
}, function(e) {
window.alert("Scanning failed: " + e);
});
}
}
});
}
Profile:
_id: Pe0t3K8GG8,
videos: [
{id:'HdaZ8rDAmy', url:'VIDURL', rank: 2},
{id:'22vZ8mj9my', url:'VIDURL2', rank: 0},
{id:'8hyTlk8H^6', url:'VIDURL3', rank: 1},
]
The profile is displayed together with the list of videos. I have a Drag & Drop which updates the videos rank using a Server Method.
1) the database updates correctly on Drop.
2) To sort the videos Array - I declare a helper on the Profile Template and SORT the videos array based on a custom comparison function.
Template.Profile.helpers({
'videosSorted': function(){
let videos = (this.videos);
let videosSorted = videos.sort(function(a, b) {
return parseFloat(a.rank) - parseFloat(b.rank);
});
return videosSorted;
}
});
Problem:
A) In Blaze the {{#each videosSorted}} does not reactively update.
If I F5 refresh then i can see the new order.
I think the issue is because I am providing videosSorted which does not update on changes to the document in the db.
How can I make videosSorted reactive?
Update:
All related code:
Iron Router Controller - I subscribe and set the data context for the layout
ProfileController = RouteController.extend({
subscriptions: function() {
this.subscribe('profile',this.params.slug).wait();
},
data: function () {
//getting the data from the subscribed collection
return Profiles.findOne({'slug':this.params.slug});
},
})
Publication:
Meteor.publish('profile', function (slug) {
const profile = Profiles.find({"slug":slug});
if(profile){
return profile;
}
this.ready();
});
The Profile HTML template:
<template name="Profile">
<ul class="sortlist">
{{#each videosSorted}}
{{> Video}}
{{/each}}
</ul>
</template>
I am using mrt:jquery-ui - sortable function
Template.Profile.onRendered(function () {
thisTemplate = this;
this.$('.sortlist').sortable({
stop: function(e, ui) {
el = ui.item.get(0);
before = ui.item.prev().get(0);
after = ui.item.next().get(0);
if(!before) {
newRank = Blaze.getData(after).rank - 1
} else if(!after) {
newRank = Blaze.getData(before).rank + 1
}
else {
newRank = (Blaze.getData(after).rank +
Blaze.getData(before).rank) / 2
}
let queryData = {
_id: thisTemplate.data._id, //the id of the profile record
videos_objId: Blaze.getData(el).objId, //the id of the sub document to update
new_rank: newRank //the new rank to give it
};
//Update the sub document using a server side call for validation + security
Meteor.call("updateVideoPosition", queryData, function (error, result) {
if(!result){
console.log("Not updated");
}
else{
console.log("successfully updated Individual's Video Position")
}
});
}
})
});
And finally the Meteor method that does the updating
'updateVideoPosition': function (queryData){
let result = Individuals.update(
{_id: queryData._id, 'videos.objId': queryData.videos_objId },
{ $set:{ 'videos.$.rank' : queryData.new_rank } }
)
return result;
}
Note :
As i mentioned - the database updates correctly - and if i have an Incognito window open to the same page - i see the videos reactivly (magically !) switch to the new order.
The schema
const ProfileSchema = new SimpleSchema({
name:{
type: String,
}
videos: {
type: [Object],
optional:true,
},
'videos.$.url':{
type:String,
},
'videos.$.rank':{
type:Number,
decimal:true,
optional:true,
autoform: {
type: "hidden",
}
},
'videos.$.subCollectionName':{
type:String,
optional:true,
autoform: {
type: "hidden",
}
},
'videos.$.objId':{
type:String,
optional:true,
autoform: {
type: "hidden",
}
}
});
I came up with really crude solution, but I don't see other options right now. The simplest solution I can think of is to rerender template manually:
Template.Profile.onRendered(function () {
var self = this;
var renderedListView;
this.autorun(function () {
var data = Template.currentData(); // depend on tmeplate data
//rerender video list manually
if (renderedListView) {
Blaze.remove(renderedListView);
}
if (data) {
renderedListView = Blaze.renderWithData(Template.VideoList, data, self.$('.videos-container')[0]);
}
});
});
Template.VideoList.onRendered(function () {
var tmpl = this;
tmpl.$('.sortlist').sortable({
stop: function (e, ui) {
var el = ui.item.get(0);
var before = ui.item.prev().get(0);
var after = ui.item.next().get(0);
var newRank;
if (!before) {
newRank = Blaze.getData(after).rank - 1
} else if (!after) {
newRank = Blaze.getData(before).rank + 1
}
else {
newRank = (Blaze.getData(after).rank +
Blaze.getData(before).rank) / 2
}
let queryData = {
_id: tmpl.data._id, //the id of the profile record
videos_objId: Blaze.getData(el).objId, //the id of the sub document to update
new_rank: newRank //the new rank to give it
};
//Update the sub document using a server side call for validation + security
Meteor.call("updateVideoPosition", queryData, function (error, result) {
if (!result) {
console.log("Not updated");
}
else {
console.log("successfully updated Individual's Video Position")
}
});
}
});
});
Template.VideoList.helpers({
videosSorted: function () {
return this.videos.sort(function (a, b) {
return a.rank - b.rank;
});
}
});
And HTML:
<template name="Profile">
<div class="videos-container"></div>
</template>
<template name="VideoList">
<ul class="sortlist">
{{#each videosSorted}}
<li>{{url}}</li>
{{/each}}
</ul>
</template>
Reativeness was lost in your case because of JQuery UI Sortable. It doesn't know anything about Meteor's reactiveness and simply blocks template rerendering.
Probably you should consider using something more adopted for Meteor like this (I am not sure it fits your needs).
We have a CMS so I don't have access to the header of the HTML page which gets rendered for our extjs implementation. So I had to make a workaround which is like this:
Ext.local = {};
var lang = {
initLang: function (revisionNr) {
var local = localStorage.getItem('localLang')
if (!local) {
AjaxHandlerByClass('ajax/lang/webuser/init', {}, this.loadLangRemote);
} else {
local = JSON.parse(local);
if (local.revisionNr == config.revisionNr && local.lang == config.lang) {
console.log('loading local lang variables');
if (local.date < new Date().getTime() - (24 * 60 * 60 * 1000) * 2) {//2 day caching time before retry
delete window.localStorage.localLangBackup;
}
this.loadLangLocal(local);
} else {
delete window.localStorage.localLang;
AjaxHandlerByClass('ajax/lang/webuser/init', {}, this.loadLangRemote);
}
}
},
loadLangRemote: function (data) {
data.revisionNr = config.revisionNr;
data.lang = config.lang;
data.date = new Date().getTime();
lang.loadLangLocal(data);
localStorage.setItem('localLang', JSON.stringify(data));
},
loadLangLocal: function (data) {
var jsElm = document.createElement("script");
jsElm.type = "application/javascript";
jsElm.src = 'js/freetext-deploy.min.js?rev={/literal}{$revisionNr}{literal}';
document.getElementsByTagName('head')[0].appendChild(jsElm);
Ext.Date.defaultFormat = 'd-m-Y';
if (!debug) {
Ext.Loader.config.disableCaching = true;
}
Ext.application({
name: 'freetextOrder',
appFolder: 'modules/extjs/freetextOrder/app',
controllers: [
'Main'
],
launch: function () {
var freetextOrder = Ext.create('Ext.container.Container', {
renderTo: Ext.get('freetextOrderDiv'),
layout: 'fit',
id: 'catalogAdministrationDiv_ext',
height: 800,
cls: 'x-dig-override',
items: [Ext.create('freetextOrder.view.base.MainView', {})],
layout:'fit'
});
}
});
Ext.local = data;
}
};
lang.initLang();
The problem I'm having is that the minified version gets ignored completely. I see it load on the http request but extjs ignores them.... even though I can see the objects are being created after include (via console log)
Anyone any idea how I can achieve this?
as i see none found the answer so i post my own here wich i came up with.
Since i could for the love of god not load the damn thing i refactored the loader and exported it into a Js. file. wich i reqired and called later on in code.
exported lang.js file:
Ext.define('Lang', {
singleton: true,
ApplicationConf: null,
Launch: function (launchConfig) {
this.ApplicationConf = launchConfig;
var local = localStorage.getItem('localLang');
var me = this;
this.loadLangRemote = function (data) {
debugger;
data.revisionNr = config.revisionNr;
data.lang = config.lang;
data.date = new Date().getTime();
me.loadLangLocal(data);
localStorage.setItem('localLang', JSON.stringify(data));
};
this.loadLangLocal = function (data) {
Ext.local = data;
Ext.lang = function (langId) {
if (Ext.local[langId]) {
return Ext.local[langId];
}
delete window.localStorage.localLang;
localStorage.setItem('localLangBackup', true);
return langId;
}
Ext.application(me.ApplicationConf);
};
if (!local) {
Ext.Ajax.request({
url: 'ajax/lang/webuser/init',
params: {
sid: sid,
},
success: function (data) {
debugger;
me.loadLangRemote(Ext.JSON.decode(data.responseText));
}
})
} else {
local = JSON.parse(local);
if (local.revisionNr == config.revisionNr && local.lang == config.lang) {
console.log('loading local lang variables');
if (local.date < new Date().getTime() - (24 * 60 * 60 * 1000) * 2) {//2 day caching time before retry
delete window.localStorage.localLangBackup;
}
debugger;
me.loadLangLocal(local);
} else {
delete window.localStorage.localLang;
Ext.Ajax.request({
url: 'ajax/lang/webuser/init',
params: {
sid: sid,
},
success: function (data) {
me.loadLangRemote(Ext.JSON.decode(data.responseText));
}
})
}
}
},
})
And IMPORTANT was to add the
Ext.onReady(function () {
Lang.Launch({
name: 'catalogAdministration',
appFold....
To the call of the Launch function in code, bacause it would have been not defined at run time. i added the file to the minified file first and call the Lang.Launch instead Ext.Application.
Hope somone has use of my solution :)
I'm having a hard time understanding the whole process of uploading images to a certain Meteor collection eg.(the belongs_to and has_one association with rails).
I have a portfolioitem collection, this is the file:
PortfolioItems = new Mongo.Collection('portfolioItems');
ownsDocument = function(userId, doc) {
return doc && doc.userId === userId;
}
PortfolioItems.allow({
update: function(userId, portfolioItem) { return ownsDocument(userId, portfolioItem); },
remove: function(userId, portfolioItem) { return ownsDocument(userId, portfolioItem); },
});
Meteor.methods({
portfolioItemInsert: function(portfolioItemAttributes) {
check(Meteor.userId(), String);
check(portfolioItemAttributes, {
title: String
});
var portfolioItemWithSameTitle = PortfolioItems.findOne({ title: portfolioItemAttributes.title});
if (portfolioItemWithSameTitle) {
return {
portfolioItemExists: true,
_id: portfolioItemWithSameTitle._id
}
}
var user = Meteor.user();
var portfolioItem = _.extend(portfolioItemAttributes, {
userId: user._id,
submitted: new Date()
});
var portfolioItemId = PortfolioItems.insert(portfolioItem);
return {
_id: portfolioItemId
};
}
});
This is the submit.js template for submitting portfolio items:
Template.submit.events({
'submit #submit-form': function(e) {
e.preventDefault();
var portfolioItem = {
title: $(e.target).find('#submit-title').val()
};
Meteor.call('portfolioItemInsert', portfolioItem, function(error, result) {
if (error) {
return alert(error.reason);
}
if(result.portfolioItemExists) {
alert('Title already taken!');
pause();
}
Router.go('portfolioItemPage', {_id: result._id});
});
}
});
Did you give a try to FSCollection? if not i think its a good option to accomplish this.
You can just declare the collection.
I Suggest you to use GridFS.
just run this 2 commands
meteor add cfs:standard-packages
meteor add cfs:gridfs
Declare the collections like any others.
Images = new FS.Collection("Images", {
stores: [new FS.Store.GridFS("Images")]
});
And you can associate the Simple collection with the FSCollection using metadata.
Template.exampe.events({
'click #addImage':function(){
var file = $('#inputPng').get(0).files[0],
fsFile = new FS.File(file);
fsFile.metadata = {
ownerId:Meteor.userId(),
title:$(e.target).find('#submit-title').val()
}
Images.insert(fsFile,function(err,result){
if(!err){
console.log(result)
}
})
}
})
At this moment the README on the fsCollection its empty so I made a little DEMO about this.