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

CORS Error on image upload from Android app #149

Open mikkelking opened 7 years ago

mikkelking commented 7 years ago

I am getting this error on a file upload from an Android app. It works without problem from a browser.

XMLHttpRequest cannot load http://10.2.14.78: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:12136' is therefore not allowed access. The response had HTTP status code 500.

I think I have followed the guidance properly, and my mobile-config.js includes both these lines:

App.accessRule('blob:*');
App.accessRule('*');

I don't know what I am missing. Presumably the reference to 'meteor.local' equates to 'localhost:12136' Hopefully this is something simple

Here is my config:

// Methods for access to Photos
Images = new FileCollection('images', {
  resumable: true,
  resumableIndexName: 'imageindex',
  http: [
    {
      method: 'get',
      path: '/md5/:md5',
      lookup: function(params, query) {
        if (debug)
          console.log("Fetching image with md5="+params.md5);
        return {
          md5: params.md5
        };
      }
    },
    {
      method: 'get',
      path: '/image/:_mid',
      lookup: function(params, query) {
        if (debug)
          console.log("Fetching image with _mid="+params._mid);
        return {
          "metadata._mid": params._mid,
          "filename": "image"
        };
      }
    },
    { method: 'put',  // Enable a PUT endpoint
      path: '/image/:_mid',  // this will be at route "/gridfs/images/image/:md5"
      lookup: function (params, query) {  // uses express style url params
        return {
          "metadata._mid": params._mid,
          "filename": "image"
        }
      },
      handler: function (req, res, next) {
         if (req.headers && req.headers.origin) {
           res.setHeader('Access-Control-Allow-Origin', 'http://meteor.local'); // For Cordova
           res.setHeader('Access-Control-Allow-Credentials', true);
         }
         next();
      }
    },
    { 
      method: 'options',  // Enable an OPTIONS endpoint (for CORS)
      path: '/image/:_mid',
      lookup: function(params, query) {
        if (debug)
          console.log("Getting options for image with _mid="+params._mid);
        if (!params._mid)
          return{
            "_id": null
          }
        return {
          "metadata._mid": params._mid,
          "filename": "image"
        }
      },
      handler: function (req, res, next) {  // Custom express.js handler for OPTIONS
        if (debug)
          console.log("In handler for image");
         res.writeHead(200, {
            'Content-Type': 'image/jpeg',
            'Access-Control-Allow-Origin': 'http://meteor.local',  // For Cordova
            'Access-Control-Allow-Credentials': true,
            'Access-Control-Allow-Headers': 'x-auth-token, user-agent',
            'Access-Control-Allow-Methods': 'GET, PUT'
         });
         res.end();
         return;
      }
    },
    {
      method: 'get',
      path: '/id/:_id',
      lookup: function(params, query) {
        if (debug)
          console.log("Fetching image with _id="+params._id);
        return {
          _id: params._id
        };
      }
    }
  ]
});
vsivsi commented 7 years ago

Hi, I'm not a Cordova developer myself, so I can only help in a general sense. Keep in mind that CORS is a mechanism for working around client side security restrictions. The server-side doesn't care where the requests are coming from, it is just playing along with the client in explicitly okaying certain types of cross-origin requests with certain headers, etc.

Eyeballing your code quickly, it looks like you are trying to upload with resumable.js, which uses POST requests to send each chunk of the upload. Your code is telling CORS to accept PUT requests, which resumable.js does not use. Resumable.js also uses HEAD requests to determine if the server already has a given chunk. Honestly not sure if those need to be explicitly enabled, or if they fall under GET (my guess is that GET is sufficient).

One approach is to initially set Access-Control- header values to '*', get things working that way, and then tighten them down to more specific values, one at a time, so when something breaks you know what change did it.

You can find the route definitions the resumable server-side support defines here. Your CORS setup needs to enable those requests.

In general, getting this CORS stuff right is tricky and details really matter. If you are not already fully familiar with it, I recommend you spend some time to dig into it a bit. Here's one decent place to start: https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS

Hope that helps.