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

Can't get CORS to work on Cordova #158

Closed rsmelo92 closed 7 years ago

rsmelo92 commented 7 years ago

Hello, I'm trying to upload an image on cordova and I'm getting the error: "XMLHttpRequest cannot load http://192.168.0.6:3000/gridfs/images/_resumable. Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:12096' is therefore not allowed access. The response had HTTP status code 500."

Although I've setted the headers just like the documentation shows:

http: [
        {
            method: 'head',
            path: '/id/:_id',
            lookup: function (params, query) { 
                return {_id: new Mongo.ObjectID(params._id._str)};
            }, 
            handler: function (req, res, next) {
                if (req.headers && req.headers.origin) {
                    res.setHeader('Access-Control-Allow-Origin', req.headers.origin );
                    res.setHeader('Access-Control-Allow-Credentials', true);
                }
            next();
            }
        },
        {
            method: 'get',
            path: '/id/:_id',
            lookup: function (params, query) {

                //console.log(Mongo);
                //console.log("params._id._str:", params._id._str);

                return {_id: new Mongo.ObjectID(params._id._str)};
            },
            handler: function (req, res, next) {
                if (req.headers && req.headers.origin) {
                    res.setHeader('Access-Control-Allow-Origin', '*'); // For Cordova
                    res.setHeader('Access-Control-Allow-Credentials', true);
                }
                next();
            }
        },
        { 
            method: 'put',  // Enable a PUT endpoint
            path: '/id/:_id',
            lookup: function (params, query) {  // uses express style url params
                return {_id: new Mongo.ObjectID(params._id._str)};
            },
            handler: function (req, res, next) {
                if (req.headers && req.headers.origin) {
                    res.setHeader('Access-Control-Allow-Origin', '*'); // For Cordova
                    res.setHeader('Access-Control-Allow-Credentials', true);
                }
                next();
            }
        },
        { 
            method: 'post',  // Enable a POST endpoint
            path: '/id/:_id',
            lookup: function (params, query) { 
                return {_id: new Mongo.ObjectID(params._id._str)};
            },
            handler: function (req, res, next) {
                if (req.headers && req.headers.origin) {
                    res.setHeader('Access-Control-Allow-Origin', 'req.headers.origin'); // For Cordova
                    res.setHeader('Access-Control-Allow-Credentials', true);
                }
                next();
            }
        },
        {
            method: 'options',  // Enable an OPTIONS endpoint (for CORS)
            path: '/id/:_id',
            lookup: function (params, query) {  // uses express style url params
                return {_id: new Mongo.ObjectID(params._id._str)};
            },
            handler: function (req, res, next) {  // Custom express.js handler for OPTIONS
               res.writeHead(200, {
                  'Content-Type': 'text/plain',
                  'Access-Control-Allow-Origin': 'req.headers.origin',  // For Cordova
                  'Access-Control-Allow-Credentials': true,
                  'Access-Control-Allow-Headers': 'user-agent',
                  'Access-Control-Allow-Methods': 'PUT,POST, HEAD, GET'
               });
               res.end();
               return;
            }
      }
    ],

And I've setted this in the mobile-configs.js App.accessRule('blob:*'); App.accessRule('*');

I still cant get it to work, can someone help me?

vsivsi commented 7 years ago

Hi, I'm not a Cordova expert, but if you are using resumable, it has a different URI endpoint.

Instead of /id/:_id

It uses /_resumable

So you'll probably need to add an OPTIONS enpoint for that path as well.

rsmelo92 commented 7 years ago

Thank you for your fast answer and of course for this great package, but I'm still unable to fix this... I've done what you suggested but my app is never setting the headers with this option setted: `{

method: 'options', 
            path: '/_resumable',
        handler: function (req, res, next) {   
           res.writeHead(200, {
              'Content-Type': 'text/plain',
              'Access-Control-Allow-Origin': 'req.headers.origin', 
              'Access-Control-Allow-Credentials': true,
              'Access-Control-Allow-Headers': 'user-agent',
              'Access-Control-Allow-Methods': 'PUT,POST, HEAD, GET'
           });
           res.end();
           return;
        }
  }`

EDIT: now I can get to set the headers but I'm getting this error, and I cant find it anywhere: 'Error in Dicer, no file found in POST' what does that means?

vsivsi commented 7 years ago

Dicer is the MIME-multipart parser that file-collection uses to handle POST requests from resumable.js on the server-side.

That error is emitted from: https://github.com/vsivsi/meteor-file-collection/blob/b668f4a13b8ea21d13751d65caa74e3d28477c96/src/http_access_server.coffee#L99-L101

This case indicates that a POST request is coming in, but there is no file data encoded in the request data. The POST is built-up on the client side by resumable.js, file-collection doesn't touch that, so I'm guessing that there's still something wrong with the Cordova HTTP configuration such that the request body isn't being transmitted properly.

I'm not a Cordova developer, so I don't know what tools you have to debug this, but you somehow need to be able to inspect the HTTP POST requests coming out of resumable.js on the client to see what they look like, and verify that they are being transmitted correctly.

rsmelo92 commented 7 years ago

Apparently my resumable here is making a GET not a POST, because all the console logs are coming from the get only... It never passes through the POST consoles log.

********GET********
I20170414-17:51:02.838(-3)? --->> params {}
I20170414-17:51:02.838(-3)? --->> query { resumableChunkNumber: '1',
I20170414-17:51:02.839(-3)?   resumableChunkSize: '1048576',
I20170414-17:51:02.839(-3)?   resumableCurrentChunkSize: '16117',
I20170414-17:51:02.839(-3)?   resumableTotalSize: '16117',
I20170414-17:51:02.840(-3)?   resumableType: 'image/jpeg',
I20170414-17:51:02.840(-3)?   resumableIdentifier: '2519e10fd606753910f409c1',
I20170414-17:51:02.840(-3)?   resumableFilename: '1492203042655-cropped.jpg',
I20170414-17:51:02.840(-3)?   resumableRelativePath: '1492203042655-cropped.jpg',
I20170414-17:51:02.840(-3)?   resumableTotalChunks: '1' }

it goes through HEAD, then GET, then OPTIONS, maybe I'm doing something wrong? Should it pass through only POST instead of GET? How can I do it?

vsivsi commented 7 years ago

Hi, between the facts that I'm not a Cordova dev, I can't see your code, and I'm not the author of (nor do I support) the client-side of resumable.js, I don't think I can be of much more help to you.

From what I can tell, the problem here has something to do with the interaction between Cordova and resumable, neither of which I can support myself. I wish I had a better answer. You might try searching the issues in the resumable.js repo, others may have run into this and figured out a solution.

rsmelo92 commented 7 years ago

Alright, Thank you for your support and your time!

rsmelo92 commented 7 years ago

Sorry, just one more thing, is there any example of this package inserting image using server side nodejs stream?

vsivsi commented 7 years ago

Just the example code for the fc.importFile() method.

https://github.com/vsivsi/meteor-file-collection#fcimportfilefilepath-file-callback

vsivsi commented 7 years ago

If you want to start with your own stream, rather than a file path, you can look to the source for this method here: https://github.com/vsivsi/meteor-file-collection/blob/master/src/gridFS_server.coffee#L342-L352

It's coffeescript, but a pretty simple function...

rsmelo92 commented 7 years ago

Thank you once again for your amazing help!

rsmelo92 commented 7 years ago

Hey it's me again. We were able to find a solution. It needed two important steps. First the headers that worked for me were different from the readme:

http: [
        {
            method: 'get',
            path: '/id/:_id',
            lookup: function (params, query) {
                return {_id: new Mongo.ObjectID(params._id._str)};
            },
        },
        {
            method: 'post',  // Enable a POST endpoint
            path: '/_resumable',  // this will be at route "/gridfs/images/_resumable"
            lookup: function (params, query) {  // uses express style url params
                return { };       // a query mapping url to images
            },
            handler: function (req, res, next) {
                if (req.headers && req.headers.origin) {
                    res.setHeader('Access-Control-Allow-Origin', req.headers.origin); // For Cordova
                    res.setHeader('Access-Control-Allow-Credentials', true);
                }
                next();
            }
        },
        {
            method: 'head',  // Enable an HEAD endpoint (for CORS)
            path: '/_resumable',  // this will be at route "/gridfs/images/_resumable/"
            lookup: function (params, query) {  // uses express style url params
                return { };       // a query mapping url to images
            },
            handler: function (req, res, next) {  // Custom express.js handler for HEAD
               if (req.headers && req.headers.origin) {
                    res.setHeader('Access-Control-Allow-Origin', req.headers.origin); // For Cordova
                    res.setHeader('Access-Control-Allow-Credentials', true);
                }
                next();
            }
        },
        {
            method: 'options',  // Enable an OPTIONS endpoint (for CORS)
            path: '/_resumable',  // this will be at route "/gridfs/images/_resumable/"
            lookup: function (params, query) {  // uses express style url params
                return { };       // a query mapping url to images
            },
            handler: function (req, res, next) {  // Custom express.js handler for OPTIONS
                res.writeHead(200, {
                    'Content-Type': 'text/plain',
                    'Access-Control-Allow-Origin': req.headers.origin,  // For Cordova
                    'Access-Control-Allow-Credentials': true,
                    'Access-Control-Allow-Headers': 'x-auth-token, user-agent',
                    'Access-Control-Allow-Methods': 'GET, POST, HEAD, OPTIONS'
                });
                res.end();
                return;
            }
        }
    ],

And the second step was that we were trying to upload a file type, and the code only worked when we converted it to a blob type. So if someone is struggling with the same problem try this: change the http headers and use a blob not a file!

vsivsi commented 7 years ago

Great news! Thanks for following-up to let me and future Cordova devs know what worked for you!

If you want to submit a PR to update/extend the docs for CORS/Cordova, I'd happily accept it!

rsmelo92 commented 7 years ago

Sorry for taking too long, of course, I will submit a PR to help with that!