vsivsi / meteor-file-collection

Extends Meteor Collections to handle file data using MongoDB gridFS.
http://atmospherejs.com/vsivsi/file-collection
Other
159 stars 37 forks source link

Cannot read property 'contentType' of undefined #88

Closed jdmswong closed 8 years ago

jdmswong commented 8 years ago

When the app tries to access downloaded files, it throws this error:

W20160217-22:44:55.526(0)? (STDERR) TypeError: Cannot read property 'contentType' of undefined
W20160217-22:44:55.526(0)? (STDERR)     at FileCollection.get (packages/vsivsi_file-collection/src/http_access_server.coffee:184:45)
W20160217-22:44:55.526(0)? (STDERR)     at Layer.handle [as handle_request] (/home/vagrant/.meteor/packages/vsivsi_file-collection/.1.3.1.tpoxtp++os+web.browser+web.cordova/npm/node_modules/express/lib/router/layer.js:95:5)
W20160217-22:44:55.527(0)? (STDERR)     at next (/home/vagrant/.meteor/packages/vsivsi_file-collection/.1.3.1.tpoxtp++os+web.browser+web.cordova/npm/node_modules/express/lib/router/route.js:131:13)
W20160217-22:44:55.527(0)? (STDERR)     at next (/home/vagrant/.meteor/packages/vsivsi_file-collection/.1.3.1.tpoxtp++os+web.browser+web.cordova/npm/node_modules/express/lib/router/route.js:125:14)
W20160217-22:44:55.527(0)? (STDERR)     at Route.dispatch (/home/vagrant/.meteor/packages/vsivsi_file-collection/.1.3.1.tpoxtp++os+web.browser+web.cordova/npm/node_modules/express/lib/router/route.js:112:3)
W20160217-22:44:55.527(0)? (STDERR)     at Layer.handle [as handle_request] (/home/vagrant/.meteor/packages/vsivsi_file-collection/.1.3.1.tpoxtp++os+web.browser+web.cordova/npm/node_modules/express/lib/router/layer.js:95:5)
W20160217-22:44:55.527(0)? (STDERR)     at /home/vagrant/.meteor/packages/vsivsi_file-collection/.1.3.1.tpoxtp++os+web.browser+web.cordova/npm/node_modules/express/lib/router/index.js:277:22
W20160217-22:44:55.527(0)? (STDERR)     at param (/home/vagrant/.meteor/packages/vsivsi_file-collection/.1.3.1.tpoxtp++os+web.browser+web.cordova/npm/node_modules/express/lib/router/index.js:349:14)
W20160217-22:44:55.527(0)? (STDERR)     at param (/home/vagrant/.meteor/packages/vsivsi_file-collection/.1.3.1.tpoxtp++os+web.browser+web.cordova/npm/node_modules/express/lib/router/index.js:365:14)
W20160217-22:44:55.528(0)? (STDERR)     at Function.process_params (/home/vagrant/.meteor/packages/vsivsi_file-collection/.1.3.1.tpoxtp++os+web.browser+web.cordova/npm/node_modules/express/lib/router/index.js:410:3)

generated from this line

server/startup.js

    return myData.allow({
        insert: function(userId, file) {
            var ref;
            file.metadata = (ref = file.metadata) != null ? ref : {};
            file.metadata._auth = {
                owner: userId
            };
            return true;
        },
        remove: function(){return true},
        read: function(userId, file) {
            var ref, ref1;
            if (
                (
                    (ref = file.metadata) != null ?
                        (ref1 = ref._auth) != null ? ref1.owner : void 0
                    : void 0
                ) && userId !== file.metadata._auth.owner
            ) {
                return false;
            }
            return true;
        },

        write: function(){return true}
    });

client/lib/startup.js


Meteor.startup(function() {

    myData.resumable.on('fileAdded', function (file) {
        Session.set(file.uniqueIdentifier, 0);
        return myData.insert({
            _id: file.uniqueIdentifier,
            filename: file.fileName,
            contentType: file.file.type
        }, function (err, _id) {
            if (err) {
                console.warn("File creation failed!", err);
                return;
            }
            return myData.resumable.upload();
        });
    });
    myData.resumable.on('fileProgress', function (file) {
        return Session.set(file.uniqueIdentifier, Math.floor(100 * file.progress()));
    });
    myData.resumable.on('fileSuccess', function (file) {
        return Session.set(file.uniqueIdentifier, void 0);
    });
    return myData.resumable.on('fileError', function (file) {
        console.warn("Error uploading", file.uniqueIdentifier);
        return Session.set(file.uniqueIdentifier, void 0);
    });
});

client/.../uploads.js

var shorten = function(name, w) {
    if (w == null) {
        w = 16;
    }
    if (w % 2) {
        w++;
    }
    w = (w - 2) / 2;
    if (name.length > w) {
        return name.slice(0, +w + 1 || 9e9) + '...' + name.slice(-w - 1);
    } else {
        return name;
    }
};

Template.uploads.onRendered(function() {
    myData.resumable.assignDrop($('.fileDrop'));
});

Template.uploads.onCreated(function () {
    this.subscribe('allData', Meteor.userId());
});

Template.uploads.helpers({
    dataEntries: function() {
        return myData.find({});
    },
    shortFilename: function(w) {
        var ref;
        if (w == null) {
            w = 16;
        }
        if ((ref = this.filename) != null ? ref.length : void 0) {
            return shorten(this.filename, w);
        } else {
            return "(no filename)";
        }
    },
    owner: function() {
        var ref, ref1;
        return (ref = this.metadata) != null ? (ref1 = ref._auth) != null ? ref1.owner : void 0 : void 0;
    },
    id: function() {
        return "" + this._id;
    },
    link: function() {
        return myData.baseURL + "/md5/" + this.md5;
    },
    uploadStatus: function() {
        var percent;
        percent = Session.get("" + this._id);
        if (percent == null) {
            return "Processing...";
        } else {
            return "Uploading...";
        }
    },
    formattedLength: function() {
        return numeral(this.length).format('0.0b');
    },
    uploadProgress: function() {
        var percent;
        return percent = Session.get("" + this._id);
    },
    isImage: function() {
        var types;
        types = {
            'image/jpeg': true,
            'image/png': true,
            'image/gif': true,
            'image/tiff': true
        };
        return (types[this.contentType] != null) && this.md5 !== 'd41d8cd98f00b204e9800998ecf8427e';
    },
    loginToken: function() {
        Meteor.userId();
        return Accounts._storedLoginToken();
    },
    userId: function() {
        return Meteor.userId();
    }
});

lib/collections/uploads.js

myData = FileCollection({
    resumable: true,
    http: [
        {
            method: 'get',
            path: '/:md5',
            lookup: function(params, query) {
                return {
                    md5: params.md5
                };
            }
        }
    ]
});
vsivsi commented 8 years ago

Hi, that shouldn't be possible... This line ensures that a 404 is returned for all non-matching files.

I've never seen this case, and it should have come up again and again by now...

Does your fc.allow( read: function (...) {...}) get called for that request? If so, is the file parameter valid at that point? That function is called from here with req.gridFS.

It seems like at some point something is overwriting req.gridFS with invalid data after the database lookup has occurred.

If you create a project with your entire app (or preferably a simplified version that still generates the error) and include Detailed instructions for reproducing it, I'll look into it tonight.

jdmswong commented 8 years ago

yes, it's executed with userId = null and file =

{ _id: { _str: 'c4019a1e328fcba5e9cf8597' },
length: 0,
 md5: 'd41d8cd98f00b204e9800998ecf8427e',
uploadDate: Thu Feb 18 2016 01:01:01 GMT+0000 (UTC),
chunkSize: 2096128,
filename: 'me.jpeg',
metadata: { _auth: { owner: '7CNmL45KWE9ErRKad' } },
aliases: [],
contentType: 'image/jpeg' }

length = 0, but the fs.chunks collection contains data

vsivsi commented 8 years ago

Oh, that's an empty file. Hmm, there may be a corner case here... The sample apps don't provide links for empty files, so this can't happen there. Let me think about this.

jdmswong commented 8 years ago

I have a distilled project ready for replication. Please email me at jdmswong ( a-t ) gmail.com with directions on how to send to you

vsivsi commented 8 years ago

Can you create a repo on github?

vsivsi commented 8 years ago

Actually I just modified the sample app to create a zero length file and it downloads just fine, so something else is going on here...

jdmswong commented 8 years ago

broken version repo: https://github.com/jdmswong/file-collection-error

vsivsi commented 8 years ago

I'm able to reproduce this, thanks. I will look into it later tonight.

vsivsi commented 8 years ago

There are two problems that I can see:

The first is that you are using the wrong path when you set up the collection. This line should be: path: '/md5/:md5', Without that, the routes are all messed up, and the error in the OP is resulting from some file-collection routes running but not others.

The second is that you aren't doing anything to authenticate your HTTP requests, so even once the route is correct, the GETs are failing with 403 errors because your allow rule requires user authentication, but you aren't supplying the token in your HTTP GET requests.

You should read this: https://github.com/vsivsi/meteor-file-collection#http-authentication

And then look at this code in the sample app: https://github.com/vsivsi/meteor-file-sample-app/blob/master/sample.coffee#L65-L70

I don't think there's any bug in file-collection here, just normal app debugging and figuring out how a new package works. Fortunately for you, there's a working sample app that you can study!

jdmswong commented 8 years ago

Thanks @vsivsi, it works in the error case now... but not in the original app. It cannot lookup the proper userId from the cookie in the read and write cases, but insert and remove work just fine.

On closer inspection; the cookie is not being passed into read and write requests, giving them nothing to work with. Is this something you have dealt with?

vsivsi commented 8 years ago

I don't think I really understand your question. Is it that the cookie isn't in the HTTP request? Or that the cookie is in the request, but isn't making it to the express middleware that parses it out and does the meteorUserId lookup based on the auth_token value?
https://github.com/vsivsi/meteor-file-collection/blob/master/src/http_access_server.coffee#L363-L376

If the cookie isn't in the HTTP request, then that's just a browser/client issue that you will need to figure out.

If it is in the HTTP request, but you aren't getting a userId back from it in your allow/deny, then your routes are still bad, such that the cookie parsing/handling middleware isn't getting called. Either that, or somehow the auth_token is invalid by the time it gets to the lookup.

paolo-g commented 8 years ago

@vsivsi, prepending "/md5/" to my path did the trick!

path: '/md5/:md5',

You've been really supportive regarding issues with File Collection! Thanks for creating this and being helpful! CFS didn't work in our stack so.... godsend!

vsivsi commented 8 years ago

Great, happy to help.