MEAN stack ng-upload-file

I am currently using MEAN.js to create an application and I am scaffolded with a simple object called Campaign . I would like each campaign to have an associated image. So I would like to change the CRUD interface to load the file at the end.

I added the ng-file-upload plugin to create an FE with Angular . On the Node.js side, I installed the multer plugin to help me save the file to a folder (eg. / Uploads). Thing is, I didn't quite understand the flow and I was hoping for a suggestion.

Please find below view:

<section data-ng-controller="CampaignsController">
    <div class="page-header">
        <h1>New Campaign</h1>
    </div>
    <div class="col-md-12">
        <form class="form-horizontal" data-ng-submit="create()" novalidate>
            <fieldset>
                <div class="form-group">
                    <label class="control-label" for="name">Name</label>
                    <div class="controls">
                        <input type="text" data-ng-model="name" id="name" class="form-control" placeholder="Name" required>
                    </div>
                </div>
                <div class="form-group">
                    <button ng-file-select ng-model="token">Upload the token</button>
                    <div ng-file-drop ng-model="token" class="drop-box" 
                        drag-over-class="{accept:'dragover', reject:'dragover-err', delay:100}"
                        accept="image/*">
                                Drop image file here
                    </div>
                    <div ng-no-file-drop>Image drop is not supported for this browser.</div>
                </div>
                <div class="form-group">
                    <input type="submit" class="btn btn-default">
                </div>
                <div data-ng-show="error" class="text-danger">
                    <strong data-ng-bind="error"></strong>
                </div>
            </fieldset>
        </form>
    </div>
</section>

      

Then the Angular controller action:

// Create new Campaign
    $scope.create = function() {
        // Create new Campaign object
        var campaign = new Campaigns ({
            name: this.name
        });

        $scope.$watch('token', function() {
            $scope.upload = $upload.upload({
                url: '/campaigns', //upload.PHP , node.js route, or servlet url
                method: 'POST', //Post or Put
                headers: {'Content-Type': 'multipart/form-data'},
                //withCredentials: true,
                data: campaign, //from data to send along with the file
                file: $scope.token, // or list of files ($files) for html5 only
                //fileName: 'photo' // to modify the name of the file(s)                
            }).success(function (response, status) {
                // Redirect after save
                campaign.$save(function(response) {
                    $location.path('campaigns/' + response._id);

                    // Clear form fields
                    $scope.name = '';
                }, function(errorResponse) {
                    $scope.error = errorResponse.data.message;
                }); 
            }
            ).error(function (errorResponse) {
                $scope.error = errorResponse.data;
                //$scope.error = errorResponse.data.message;
            });
        });
    };

      

Finally, the Node.js controller part:

 var mongoose = require('mongoose'),
        errorHandler = require('./errors'),
        multer = require('multer'),
        Campaign = mongoose.model('Campaign'),
        _ = require('lodash');

    /**
     * Create a Campaign
     */
    exports.create = function(req, res) {
        var campaign = new Campaign(req.body);
        campaign.user = req.user;

        multer({
            dest: './uploads/'
        });

        campaign.save(function(err) {
            if (err) {
                return res.status(400).send({
                    message: errorHandler.getErrorMessage(err)
                });
            } else {
                res.jsonp(campaign);
            }
        });
    };

      

What is currently happening is that - when I try to upload a file - the uploader doesn't wait for the file to be selected, but it will immediately send a POST request (why?). Moreover, I get a 400 response.

Any suggestion would be really appreciated!

Thanks Greetings

+3


source to share


2 answers


I partially solved the problem.

This is a new look:

<section data-ng-controller="CampaignsController">
    <div class="container">
        <div class="page-header">
            <h1>New Campaign</h1>
        </div>
        <div class="col-sm-12 col-md-4 col-md-offset-4">
            <form class="form-horizontal" data-ng-submit="create(token)" novalidate>
                <fieldset>
                    <div class="form-group">
                        <label class="control-label" for="name">Name</label>
                        <div class="controls">
                            <input type="text" data-ng-model="name" id="name" class="form-control" placeholder="Name" required>
                        </div>
                    </div>
                    <div class="form-group">
                        <label class="control-label" for="token">Token</label>
                        <div class="controls">
                            <input type="file" id="token" ng-file-select ng-model="token"/>
                            <p class="help-block">The token file must be a squared .png or .jpg image.</p>
                        </div>
                    </div>
                    <div class="form-group">
                        <div class="controls">
                            <input type="submit" class="btn btn-default col-xs-12">
                        </div>
                    </div>
                    <div class="form-group">
                        <div data-ng-show="error" class="control alert alert-danger alert-dismissible" role="alert">
                            <span data-ng-bind="error"></span>
                        </div>
                    </div>
                </fieldset>
            </form>
        </div>
    </div>
</section>

      

Then the Angular controller action:

    $scope.create = function(token) {
        // Create new Campaign object
        var campaign = new Campaigns ({
            name: this.name
        });

        $scope.upload = $upload.upload({
            url: '/campaigns',
            method: 'POST',
            headers: {'Content-Type': 'multipart/form-data'},
            //withCredentials: true,
            data: { 
                campaign: JSON.stringify(campaign) 
            },
            file: token,
            //fileName: 'token' // to modify the name of the file                
        }).success(function (response, status) {
                // Redirect after save
                $location.path('campaigns/' + response._id);

                // Clear form fields
                $scope.name = '';
                $scope.token = '';
            }
        ).error(function (errorResponse) {
               $scope.error = errorResponse.data;
            }
        );
    };

      

I am now using node multipart for Node.js controller:



export.create = function (req, res) {

var form = new multiparty.Form();
form.parse(req, function(err, fields, files) {
    //res.writeHead(200, {'content-type': 'text/plain'});
    //res.end(util.inspect({fields: fields, files: files}));
    var file = files.file[0];
    var contentType = file.headers['content-type'];
    var tmpPath = file.path;
    var extIndex = tmpPath.lastIndexOf('.');
    var extension = (extIndex < 0) ? '' : tmpPath.substr(extIndex);
    // uuid is for generating unique filenames. 
    //var fileName = uuid.v4() + extension;
    var fileName = tmpPath;
    var destPath = 'uploads/' + fileName;

    // Server side file type checker.
    if (contentType !== 'image/png' && contentType !== 'image/jpeg') {
        fs.unlink(tmpPath);
        return res.status(400).send({
            message: 'Unsupported file type'
        });
    }

    fs.rename(tmpPath, destPath, function(err) {
        if (err) {
            return res.status(400).send({
                message: 'Image is not saved'
            });
        }
        fs.unlink(tmpPath, function() {
            if (err) {
                return res.status(400).send({
                    message: 'Impossible to delete temp file'
                });
            }
        });
        console.log(destPath);
        //return res.jsonp(destPath);
    });

    var campaign = new Campaign(JSON.parse(fields.campaign[0]));
    campaign.user = req.user;

    campaign.save(function(err) {

        if (err) {
            return res.status(400).send({
                message: errorHandler.getErrorMessage(err)
            });
        } else {
            res.jsonp(campaign);
        }
    });
});

      

};

I am still getting the error, but I don't think it has anything to do with the file upload. What do you think?

/home/Maurizio/Workspace/BDF-v1/node_modules/mongoose/lib/utils.js:413 throw error; ^ Error: Unable to customize headers after sending them. at ServerResponse.OutgoingMessage.setHeader (http.js: 691: 11) at ServerResponse.header (/ home / maurizio / Workspace / bdf-v1 / node_modules / express / lib / response.js: 592: 10) on ServerResponse.send (/home/maurizio/Workspace/bdf-v1/node_modules/express/lib/response.js:144:12) in ServerResponse.jsonp (/ home / maurizio / Workspace / bdf-v1 / node_modules / express / lib / response. js: 301: 15) in Promise. (/home/maurizio/Workspace/bdf-v1/app/controllers/campaigns.server.controller.js:67:9) in Promise. (/home/maurizio/Workspace/bdf-v1/node_modules/mongoose/node_modules/mpromise/lib/promise.js:177:8)

0


source


Using res.status (400) .send ... or res.jsonp () will send data back to the client, starting with the headers. Your script executes these instructions, but subsequent ones cannot be executed since the data has already been sent to the client.

The return you have will end the execution of the method in which they were called, but the script will just continue on to the next method in which it encounters another express dispatch () function. In your case, fs.rename will send () 400, but will encounter another send () method when it reaches the campaign.save method, where it will throw an error.



Take calls returning res.status (400) .send () and instead set the message as a string variable and make a call to res.status (400) .send () in your final conditional if an error is present.

Essentially, make sure the call to send () or jsonp () can only be done once per script.

0


source







All Articles