我目前正在使用 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回复。
任何建议都会非常感激!
由于 干杯
答案 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()调用只能在脚本中进行一次。