Meteor-Files and MongoCollection

#1

Hi, I’m still trying to figure out Meteor-Files.

What I’m trying to achieve: I have a collection named “Characters” and a FilesCollection named “Images”.
Every document in “Characters” is supposed to have one image assigned to them. It would basically work like profile pictures where every user can upload his image.

Due to my previous experiences in PHP I did something like that with linking two tables in MySQL to achieve that. But I’m clueless on how to do that with Mongo/Meteor-Files.

Here is what I have so far: The file-upload-system is a very slightly modified version of what is shown on Meteor-Files-Github.

imports/api/images/images.js

// Definition of the image collection
import { Meteor } from 'meteor/meteor';
import { FilesCollection } from 'meteor/ostrio:files';

export const Images = new FilesCollection({
  collectionName: 'Images',
  allowClientCode: false, // Disallow remove files from Client
  onBeforeUpload(file) {
    // Allow upload files under 10MB, and only in png/jpg/jpeg formats
    if (file.size <= 10485760 && /png|jpg|jpeg/i.test(file.extension)) {
      return true;
    }
    return 'Please upload image, with size equal or less than 10MB';
  }
});


if (Meteor.isClient) {
  Meteor.subscribe('files.images.all');
}

 
if (Meteor.isServer) {
  // Upload sample files on server's startup:
  Meteor.startup(() => {
    Images.load('https://raw.githubusercontent.com/VeliovGroup/Meteor-Files/master/logo.png', {
      fileName: 'logo.png'
    });
  });

 
} else {
  // Subscribe to file's collections on Client
  Meteor.subscribe('files.images.all');
}

imports/api/images/methods.js

//Empty yet, but I figure I'll need methods in order 
//to link these two collections

imports/api/images/server/publications.js


import { Images } from '../images.js';

Meteor.publish('files.images.all', function () {
  return Images.find().cursor;
});

ui/components/upload_image.html
The Templates I use for uploading:

<template name="uploadForm">
{{#with currentUpload}}
Uploading <b>{{file.name}}</b>
<span id="progress">{{progress.get}}%</span>
{{else}}
<input id="fileInput" type="file" />
{{/with}}
</template>

<template name='file'>
  <img src="{{imageFile.link}}" alt="{{imageFile.name}}" width="360px" height="450px" />
  <!-- Same as: -->
  <!-- <img src="{{fileURL imageFile}}" alt="{{imageFile.name}}" /> -->

</template>

ui/components/upload_image.js

And the js-File for the templates


import { Images } from '/imports/api/images/images.js'
import { Template }    from 'meteor/templating';
import { ReactiveVar } from 'meteor/reactive-var';

Template.uploadForm.onCreated(function () {
  this.currentUpload = new ReactiveVar(false);
});

Template.uploadForm.helpers({
  currentUpload() {
    return Template.instance().currentUpload.get();
  }
});

Template.uploadForm.events({
  'change #fileInput'(e, template) {
    if (e.currentTarget.files && e.currentTarget.files[0]) {
      // We upload only one file, in case
      // multiple files were selected
      const upload = Images.insert({
        file: e.currentTarget.files[0],
        streams: 'dynamic',
        chunkSize: 'dynamic'
      }, false);

      upload.on('start', function () {
        template.currentUpload.set(this);
      });

      upload.on('end', function (error, fileObj) {
        if (error) {
          alert('Error during upload: ' + error);
        } else {
          alert('File "' + fileObj.name + '" successfully uploaded');
        }
        template.currentUpload.set(false);
      });

      upload.start();
    }
  }
});

Template.file.helpers({
  imageFile() {
    return Images.findOne();
  },
});

My character collection set up looks as follows:

imports/api/characters/characters.js

// Definition of the news collection
import { Mongo } from 'meteor/mongo';
export const Characters = new Mongo.Collection('characters');

imports/api/characters/methods.js

// Methods related to news

import { Meteor } from 'meteor/meteor';
import { check } from 'meteor/check';
import { Characters } from './characters.js';


////////////////

Meteor.methods({

  'characters.start'(name) {
    check(name, String);

    // Make sure the user is logged in before creating character
    if (! Meteor.userId()) {throw new Meteor.Error('not-authorized');}

    return Characters.insert({
      name,
      species: "",
      gender: "",
      age: "",
      occu: "",
      weight: "",
      height: "",
      statue: "",
      skincolor: "",
      haircolor: "",
      hair: "",
      eyecolor: "",
      chardesc: "",
      room: "",
      createdAt: new Date(),
      owner: Meteor.user().username,

    });
  },

  'characters.remove'(charactersId) {
    check(charactersId, String);
    Characters.remove(charactersId);
  },

  'characters.update'(name, species, gender, age, occu, weight, height, statue, skincolor, haircolor, hair, eyecolor, chardesc) {
    //check(charactersId, String);
    check(name, String);
    check(species, String);
    check(gender, String);
    check(age, String);
    check(occu, String);
    check(weight, String);
    check(height, String);
    check(statue, String);
    check(skincolor, String);
    check(haircolor, String);
    check(hair, String);
    check(eyecolor, String);
    check(chardesc, String);

  
    // user has to be logged in before updating content
    if (! Meteor.userId()) {throw new Meteor.Error('not-authorized');}
  
    Characters.update({name: name}, {  //The parentheses was closed before the $set in your code
      $set: {
        name:name, 
        species:species,
        gender:gender,
        age:age,
        occu:occu,
        weight:weight,
        height:height,
        statue:statue,
        skincolor:skincolor,
        haircolor:haircolor,
        hair:hair,
        eyecolor:eyecolor,
        chardesc:chardesc 
      },
   });
  
  
  
    },

    'charactersRoom.update'(name, room) {
      check(room, String)
      check(name, String)
      if (! Meteor.userId()) {throw new Meteor.Error('not-authorized');}

      Characters.update({name: name}, {
        $set: {
          name:name,
          room: room,
        },
      });

    },
  
  
});

imports/api/characters/server/publications.js

// Own Characters
Meteor.publish('characters.all', function () {
  if (this.userId) {
    return Characters.find({owner: Meteor.user().username});
  }
});

My question is, where to start. Even a push in the right direction would be appreciated since I’m so clueless that I don’t know what exactly I have to learn. My initial idea was to somehow use the Meteor.method ‘characters.start’ in order to somehow put the image adress value of the FilesCollection in a new image-key of the characters-collection.

Edit: Uploading and streaming images work already.

#2

Update:

I managed to assign the individual pages to the images by using the meta-key

      const upload = Images.insert({
        file: e.currentTarget.files[0],
        streams: 'dynamic',
        chunkSize: 'dynamic',
        meta: {'owner': char}
      }, false);

and then finding them by using the Parameter of the URL

Template.file.helpers({
  imageFile() {
    var char = FlowRouter.getParam('char');
    return Images.findOne({meta: {owner: char}});
  },
});

However, if the user decides to update the image the one that was uploaded first, remains.

Edit:
I solved the Updating-problem by deleting the current image, like so:

      const upload = Images.insert({
        file: e.currentTarget.files[0],
        onStart() {
        Images.remove({meta: {owner: char}});
        },
        streams: 'dynamic',
        chunkSize: 'dynamic',
        meta: {'owner': char}
            }, false);