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

Upload without using file-collection on the client? #102

Open rezaxdi opened 8 years ago

rezaxdi commented 8 years ago

Hi

I'm trying to use Meteor methods to do the insert and create an empty file collection and then upload to it using Resumable but I had no success. Can you please guide me how can I achieve this ? Because the resumable assignment should be done on client side

myFiles.resumable.assignDrop($(".fileDrop"));

But when using methods, myFiles is not accessible on client side.

Thanks in advance

vsivsi commented 8 years ago

Hi, you want a client to be able to create a new collection in the database? Doing so with file-collection is fundamentally no different than doing it with an ordinary Meteor collection. WIth that said, this is an anti-pattern. What use case do you have that requires the client to create a new collection in the database as a target to upload (as opposed to using metadata within a single collection to keep track of data). Meteor doesn't even provide an easy way to "discover" existing (non-hard coded) collections at runtime. You can do it by accessing the raw MongoDB driver, but the fact that this is not anticipated in the Meteor API tells you it is an exceptional case. I'm concerned that exposing this capability to clients creates a DoS opportunity for malicious actors because creating tens of thousands of collections in a MongoDB database will almost certainly have severe performance implications.

TDLR; Consider the possibility that this isn't straightforward because its not the best way to achieve what you are trying to do.

rezaxdi commented 8 years ago

No I don't want the client to be able to create a new collection. I want to restrict the client from accessing FileCollection object (e.g. myFiles) directly. I mean in Meteor we use Methods to let users interact with database after doing some checks , ... in each request, but I was not able to do the same thing for file-collection too. What I want to do is to move "myFiles.insert" inside a "Meteor.methods" like this :


Meteor.methods({
  'insertfile'(input){
    //Some validation on input 
    myFiles.insert({
        _id: file.uniqueIdentifier, 
        filename: file.fileName,
        contentType: file.file.type
        },
....
})

And then call the method from client side, but I don't know about it being possible at all and if it is how can I achieve that ?

vsivsi commented 8 years ago

Hi, okay, that makes more sense... I was confused by:

...use Meteor methods to do the insert and create an empty file _collection_ and then upload to it...

With that out of the way...

Have you considered writing allow/deny rules for the fc.insert()? https://github.com/vsivsi/meteor-file-collection#fcallowoptions

In Meteor, under the hood, client-side mutators are essentially Meteor method calls that invoke the allow/deny logic for validation. If you can write your "checks" in terms of allow/deny rules, then that will be your path of least resistance.

If you have determined that this can't work for you, then you can certainly write the insert into a Meteor method call. There are two possible routes I can see to get around your "no myFiles on client" issue.

1) Go ahead and instantiate myFiles on the client anyway, but completely lock it down. Don't publish any documents to it, and don't write any allow rules. There is no "autopublish" in file-collection, so everything is locked down by default. In that case, you can use the integrated resumable object. The main thing is that you will need to pass the file.uniqueIdentifier from resumable to your server-side method so that a file with the correct _id is created (forming a valid target for the resumable upload).

2) Don't use file-collection at all on the client. This seems to be what you are angling towards. In this case you will need to initialize your own resumable object on the client... You should set it up the same way that file-collection does. Once you have your own resumable object, you can use it just as you would the integrated one that file-collection provides. As above, you need to be sure to pass the file.uniqueIdentifier to the method so that resumable and the server-side insert are using the same file. See: https://github.com/vsivsi/meteor-file-collection/blob/master/src/resumable_client.coffee#L28-L46

rezaxdi commented 8 years ago

The second option is great, I just wasn't sure about what is file-collection doing during creation of resumable object and I don't know why but I forgot to analyse that resumable_client file in the source code. Thanks a lot for your time

rezaxdi commented 8 years ago

Hmm, I just face another problem. I created a resumable object on client-side and everything seems to be correct except when resumable sends a "HEAD" request instead 204 response it gets a 200 response code, so it doesn't send the "POST" request thus no upload is taking place.

I checked target, uniqueId and other options of resumable object. all are same as the default values which are working well when I use the example and the uniqueId is same both on server and client. All allow rules set to true for testing. Accroding to http_access_server file I should get a 204 response but I'm not.

vsivsi commented 8 years ago

Please commit (minimal) code reproducing this behavior into a repo and I will take a look. It's the only productive way to debug problems like this.

rezaxdi commented 8 years ago

Thanks for your reply, here is the minimal code that reproduces the problem : https://github.com/rezaxdi/file-collection-test

vsivsi commented 8 years ago

The problem is that you aren't configuring resumable to point to the correct path on the server for the file-collection you define.

In this line you set a hardcoded target: https://github.com/rezaxdi/file-collection-test/blob/master/client/main.js#L8

In your case it should be:

 target: '/gridfs/myFiles/_resumable',  // not '/gridfs/fs/_resumable'

You can have multiple file-collections in an app, and each needs its own base URL path. The "name" you assign to a file collection when you create it controls the name of the gridFS bucket, and also the root of that file-collection's URL. If you don't specify a name, then it defaults to "fs" (per gridFS convention, the default bucket name.) So your path would work fine if you hadn't given your file-collection the name "myFiles" on the server.

I tracked this down by simply copying the HEAD query from the browser network trace and pasting it into a curl command. That returned the default Meteor loading HTML page (and hence the 200), because there was no other route defined for that URL.

vsivsi commented 8 years ago

One other quick point about your test-code. These lines make the server-side insecure to a hostile client. Even though you don't load the file-collection package on the client, a malicious actor still could, and then those allow declarations would give them full access to the file-collection. I'm sure that was just an oversight in your sample app, but a dangerous one!

rezaxdi commented 8 years ago

As I had only one file-collection in the app, I hard-coded it but sometimes a simple mistake can cost you so many hours. Thanks a lot for your help and those allow rules were only for testing, I commented them to reduce the chance of anyone using that part in their app.