How to list images inside public/?

Hi. I want to list the available icons found in /public/icons/

Does it make sense to use Meteor.publish() to return something static like a list of files in a folder?

How do I get the path of /public to list its content?

I donā€™t think publish would be the right thing to use for something like that. Use a method on the server instead.

this would show your icon <img src="icons/yourIcon.png"

Could you explain why publish would not be the right thing for that? If I could use publish I could easily use this data in several routes. With a method that lists the files in a folder, the client must call the server each time I want to show the list, which is not very efficient, and it would delay displaying the results, or? The list of files does not change every day. I would say that the application must feel faster if I provide the data as part of a route instead of calling a method each time.

I know how to show one icon, that was not my question. Fortunately I found how to list the icons:

if(Meteor.isServer) {
  var fs = Npm.require("fs");
  var iconPath = process.env.PWD + '/public/icons/';
  var icons = fs.readdirSync(iconPath);
  console.log(icons);
}

Which works assuming thereā€™s no subfolders under icons/. What I donā€™t know is if process.env.PWD can be trusted to always provide the right path.

Publish is for collections and it returns a cursor. I am not sure if it works with anything else

Answering my own question, itā€™s possible to publish a list of files found in /public:

Meteor.publish('icons', function() {
  var self = this;
  var fs = Npm.require("fs");
  var iconPath = process.env.PWD + '/public/icons/';
  var icons = fs.readdirSync(iconPath);
  _.each(icons, function(icon) {
    if(icon.indexOf('icon-') == 0) {
      self.added('icons', icon, { 'url': '/icons/' + icon });
    }
  });
  this.ready();
});

Then I include

Meteor.subscribe('icons');

and

Icons = new Mongo.Collection('icons');

which seems to act as a virtual collection, since it does not get saved to disk. But it works. In the client I can then query Icons.find(), which returns the list of icons found in /public/icons, bridging the filesystem to some kind of virtual collection I can query. I could even publish some metadata (size, date, type, ā€¦) together with the url, and then query that metadata to get a subset of the icons.

6 Likes

if that works as you said, thats pretty neat!

1 Like

This definitely works! The only thing I had to change is using process.env.PWD to find the project root. That worked for me locally but blew up in production. The following worked for me both locally and in production:

var meteorRoot = fs.realpathSync( process.cwd() + '/../' );
var publicPath = meteorRoot + '/web.browser/app/';
var iconPath = publicPath + '/icons/';
1 Like

Exactly, you can make your own ā€œvirtual collectionsā€, if we want to call them that, i.e. not backed by MongoDB.

Just one suggestion: Make sure you cache the results of scanning the file system for the icons, or at least add a TODO somewhere that will get acted upon before there is a chance that a couple hundred users hit your app at a time, because then youā€™ll be in trouble :smile:
And by ā€œcacheā€ I mean even something as simple as storing the results in a var and having a function run every 5 minutes to re-list the files and update the var, if you expect the icons to ever change during runtime (as opposed to build / deploy time).

1 Like

Probably a better idea to use Meteor-CollectionFS

1 Like

For my purpose CollectionFS was too much, since we didnā€™t want to allow users to upload their own photosā€¦ only choose from the ones we supply.

Why the fs.realpathSync(...)?

Edit: ah, likely to resolve /../

Thatā€™s pretty cool! :thumbsup: Never used built in Node features in Meteor before, and havenā€™t messed around with manual publications either!

I wonder what other cool Node features there areā€¦

Iā€™m guessing Npm.require could be replaced with import fs from 'fs' right?