13

I am developing a MEAN stack web application and I want to upload file using ng2-file-upload. This is my Angular 2 code.

classroom.component.html

    <input type="file" class="form-control" name="single" ng2FileSelect [uploader]="uploader" />
    <button type="button" class="btn btn-success btn-s"
                  (click)="uploader.uploadAll()" [disabled]="!uploader.getNotUploadedItems().length">
            <span class="glyphicon glyphicon-upload"></span> Upload all
          </button><br />

classroom.component.ts

uploader:FileUploader = new FileUploader({url: "http://localhost:3000/api/material/create-material"});

In server.js

app.use(cors());
app.use('/api',api);
app.use('/api/material',material);

and in material.js

var storage = multer.diskStorage({ //multers disk storage settings
    destination: function (req, file, cb) {
        cb(null, './uploads/');
    },
    filename: function (req, file, cb) {
        var datetimestamp = Date.now();
        cb(null, file.fieldname + '-' + datetimestamp + '.' + file.originalname.split('.')[file.originalname.split('.').length -1]);
    }
});

var upload = multer({ //multer settings
    storage: storage
}).single('file');

router.post('/create-material',passport.authenticate('jwt', {session: false}),function (req, res) {

    upload(req,res,function(err){
        console.log(req.file);
        if(err){
            res.json({error_code:1,err_desc:err});
            return;
        }
        //res.json({error_code:0,err_desc:null});
    });
});

When uploading a file I get following error

XMLHttpRequest cannot load http://localhost:3000/api/material/create-material. Response to preflight request doesn't pass access control check: The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'. Origin 'http://localhost:4200' is therefore not allowed access. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.

What is the reason for this?

Claies
  • 22,124
  • 4
  • 53
  • 77
chamathabeysinghe
  • 858
  • 2
  • 13
  • 34

7 Answers7

10

Add following to your nodejs middleware -

app.use(function (req, res, next) {
    res.setHeader('Access-Control-Allow-Origin', '*');
    res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');
    res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,content-type');
    res.setHeader('Access-Control-Allow-Credentials', true);

    next();
});

The CORS supports *, null or the exact protocol + domain + port as per -> http://www.w3.org/TR/cors/#access-control-allow-origin-response-header

Server will need to validate the origin header using the regex, and then you can echo the origin value in the Access-Control-Allow-Origin response header.

AJS
  • 953
  • 7
  • 21
3

The problem here is you are running two apps

  1. Angular 2 app with port number 4200
  2. MEAN stack app with port number 3000

So when Angular 2 app tries to do any calls to MEAN stack app which has port number "3000" browser will complain as it will be treated as cross domain ajax. To solve the cross domain ajax you will have to add the following lines of code in your MEAN stack app.

app.use(function(req, res, next) {
    res.header("Access-Control-Allow-Origin", "*");
    res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
    next();
});

If you really dont like doing this, then you have to do following steps.

  1. First build the angular app to deploy directly under MEAN stack app using "ng build".
  2. This will generate "dist" folder in angular app.
  3. Move this dist folder under root folder of MEAN stack app and add following lines of code

    app.use(express.static(path.join(__dirname, 'dist'), {index: false}));
    app.get('/', function (req, res) { res.sendFile(path.join(__dirname + '/dist/index.html')); });

By doing the above steps we are running only one app that is under MEAN stack and front-end will be served from angular generated markup from dist folder.

HumbleGeek
  • 51
  • 2
3

ng2-file-upload has option to specify withcredentials for each file.

Override onAfterAddingFile and set withCredentials = false for each file.

eg: this.uploader.onAfterAddingFile = (fileItem:any) => { fileItem.withCredentials = false; }

This will solve your problem. For more details: ng2-file-upload/src/file-upload/file-item.class.ts

Jithin Sebastian
  • 511
  • 1
  • 6
  • 19
2

Your node.js code will go like this

var express=require("express");
var multer=require("multer");
var app=express();
var upload=multer({dest:"//destination where you want to upload<folder name>"});
app.post("//routing",upload.single('<variable-name>'),function(req,res){
    console.log("File Uploaded");
});
app.listen(//<port-number>);
m7913d
  • 10,244
  • 7
  • 28
  • 56
Yash Bele
  • 676
  • 7
  • 7
2

Most of the given answers here are correct for implementations where you have two apps running and your need to set correct CORS headers so they can communicate via the browser. In your particular case however, the issue is that you are using localhost for development.

The issue is that Access-Control-Allow-Origin: * doesn't match localhost. Given you've set the correct CORS headers for your OPTIONS requests as the other answers suggest, you're preflight request will return the correct headers, but chrome doesn't acknowledge localhost as a valid domain matching *, and will fail the preflight check.

To solve the issue, a quick solution is to add a fake domain to your hosts file such as local.development and then point to this domain instead of localhost. You're cross-origin requests should now work.

Ian Belcher
  • 5,583
  • 2
  • 34
  • 43
0

Quick Fix For localhost:

Use Allow-Control-Allow-Origin Chrome Extension and then add localhost in the list with *

Hope this will help.

And try using FormData for file Upload.(Angular)

 let formData:FormData = new FormData();  
 formData.append('Document',file);  
Y Munawwer
  • 126
  • 1
  • 6
-1

maybe this helps:

No 'Access-Control-Allow-Origin' - Node / Apache Port Issue

to wildcard the Access-Control-Allow-Origin just put a "*" instead of the "http://localhost:8888" origin

Community
  • 1
  • 1
flowest
  • 129
  • 2
  • 10