MEAN stack ng-upload-file

时间:2014-12-09 21:23:49

标签: angularjs node.js mean-stack

我目前正在使用 MEAN.js 来创建应用,并且搭建一个名为广告系列的简单实体。我希望每个广告系列都有相关的图片。因此,我想更改 CRUD 界面,以便上传文件到后端。

我注入 ng-file-upload 插件,以 Angular 创建FE。在 Node.js 方面,我安装了multer插件,以帮助我将文件保存到文件夹中(例如./uploads)。问题是,我不太了解流程,我希望得到一个建议。

请在下面查看:

<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>

然后,Angular控制器动作:

// 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 script, 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;
            });
        });
    };

最后,Node.js控制器部分:

 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);
            }
        });
    };

现在,会发生什么 - 当我尝试上传文件时 - 上传者不会等待文件被选中,但它会立即发送POST请求(为什么?)。而且,我收到了400回复。

任何建议都会非常感激!

由于 干杯

2 个答案:

答案 0 :(得分:0)

我部分地解决了这个问题。

这是新观点:

<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>

然后,Angular控制器动作:

    $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;
            }
        );
    };

我现在正在为Node.js控制器使用node multiparty:

exports.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);
        }
    });
});

};

我仍然收到错误,但我认为与文件上传无关。你觉得怎么样?

/home/maurizio/Workspace/bdf-v1/node_modules/mongoose/lib/utils.js:413         扔错了;               ^ 错误:发送后无法设置标头。     在ServerResponse.OutgoingMessage.setHeader(http.js:691:11)     在ServerResponse.header(/home/maurizio/Workspace/bdf-v1/node_modules/express/lib/response.js:592:10)     在ServerResponse.send(/home/maurizio/Workspace/bdf-v1/node_modules/express/lib/response.js:144:12)     在ServerResponse.jsonp(/home/maurizio/Workspace/bdf-v1/node_modules/express/lib/response.js:301:15)     在Promise。 (/home/maurizio/Workspace/bdf-v1/app/controllers/campaigns.server.controller.js:67:9)     在Promise。 (/home/maurizio/Workspace/bdf-v1/node_modules/mongoose/node_modules/mpromise/lib/promise.js:177:8)

答案 1 :(得分:0)

使用res.status(400).send ...或res.jsonp()将从头开始将数据发送回客户端。您的脚本会丢失这些语句,但由于数据已经发送到客户端,因此后续的语句无法执行。

您拥有的返回将结束执行它们被调用的方法,但脚本将继续执行下一个遇到另一个快速send()的方法。在你的情况下,fs.rename将发送()400,但是当它到达campaign.save方法时会遇到另一个send(),它会抛出错误。

调用return res.status(400).send(),然后将消息设置为字符串变量,并在最终的条件语句中调用res.status(400).send()错误存在。

基本上,确保send()或jsonp()调用只能在脚本中进行一次。