我在两个实体之间有一对多的关系:课程和文件。
因此,我设置数据库的方式是使用两个单独的集合:课程和文件。
var CourseSchema = new mongoose.Schema({
name: { type: String, required: true },
code: { type: String, required: true, unique: 1, uppercase: 1 },
files: [{ type: mongoose.Schema.Types.ObjectId, ref: 'File' }]
// more fields
});
var FileSchema = new mongoose.Schema({
name: { type: String, required: true }
// more fields
});
我有一个工作页面,允许用户在课程中上传和添加文件。同样,他们可以在同一页面上删除所选文件。
我担心的是删除所选文件的时间。选择需要删除的文件并单击提交按钮后,我发送一个DELETE请求,传递文件ID列表。下面是我删除文件的处理程序:
var fs = require('fs');
var async = require('async'),
mongoose = require('mongoose');
var models = require('../models');
exports.deleteFiles = function (req, res) {
// loop through selected file-ids
async.eachSeries(req.body.files, function (id, done) {
// remove file from File collection
models.File.findByIdAndRemove(id, function (err, file) {
if (err) {
return done(err);
} else if (!file) {
return done();
}
// remove file reference from Course document
req.course.files.pull(mongoose.Types.ObjectId(id));
req.course.save(function (err) {
if (err) {
return done(err);
}
var path = __dirname + '/../public/upl/' + req.course.id + '/' + file.name;
// remove file from filesystem
fs.stat(path, function (err, stats) {
if (err && err.code === 'ENOENT') {
return done();
} else if (err) {
return done(err);
}
if (stats.isFile()) {
fs.unlink(path, done);
}
});
});
});
}, function (err) {
if (err) {
console.log(err);
req.flash('failure', 'Unable to delete files at this time.');
} else {
req.flash('success', 'The files have been deleted successfully.');
}
res.redirect('/admin/courses/' + req.course.id + '/files');
});
};
这非常麻烦,因为我必须为每个文件ID执行几个步骤:从课程的文件数组中删除ID,从实际集合中删除文件,然后从文件系统中删除文件。同样,我在每个步骤的开头都有一些错误处理代码。
使用更少的步骤和/或更好的错误处理可以改善这一点吗?
答案 0 :(得分:1)
我会这样写,更清洁的imho,使用each
和waterfall
来迭代收集并从 findByIdAndRemoveFn传递文件 功能
var async = require('async');
var File = require('../models/File'); //assume it's File.js
exports.deleteFiles = function (req, res) {
var files = req.body.files;
var course = req.course;
var prePath = __dirname + '/../public/upl/' + course.id + '/';
async.each(files, function(fileId, cb) {
async.waterfall([
function findByIdAndRemoveFn(parallelCb) {
File.findByIdAndRemove(fileId, function(err, file) {
if(err) return parallelCb(err);
parallelCb(null, file);
});
},
function pullFn(file, parallelCb) {
course.update({$pull: {files: fileId}}, function(err) {
if(err) return parallelCb(err);
parallelCb(null, file);
});
},
function unlinkFn(file, parallelCb) {
var path = prePath + file.name;
fs.stat(path, function(err, stats) {
if(err) return parallelCb(err);
else if(stats.isFile()) fs.unlink(path, parallelCb);
else parallelCb();
});
}
], cb);
}, function(err) {
if(err) req.flash('failure', 'Unable to delete files at this time.');
else req.flash('success', 'The files have been deleted successfully.');
res.redirect('/admin/courses/' + course.id + '/files');
});
}
答案 1 :(得分:1)
另一种选择是使用async.auto
方法:
async.eachSeries(req.body.files, function (id, done) {
async.auto({
removeFileRecord: function(cb) {
models.File.findByIdAndRemove(id, function (err, file) {
if (err) return cb(err);
return cb(null, file);
});
},
pullFileFromCourse: ['removeFileRecord', function(results, cb) {
// do nothing if file did not exist
if (!results.removeFileRecord) return cb(null);
req.course.files.pull(mongoose.Types.ObjectId(id));
req.course.save(cb);
}],
unlinkFile: ['pullFileFromCourse', function(results, cb) {
var file = results.removeFileRecord;
var path = __dirname + '/../public/upl/' + req.course.id + '/' + file.name;
// remove file from filesystem
fs.stat(path, function (err, stats) {
if (err && err.code === 'ENOENT') {
return cb();
} else if (err) {
return cb(err);
}
if (stats.isFile()) {
fs.unlink(path, cb);
}
});
}];
}, done);
}, function (err) {
if (err) {
console.log(err);
req.flash('failure', 'Unable to delete files at this time.');
} else {
req.flash('success', 'The files have been deleted successfully.');
}
res.redirect('/admin/courses/' + req.course.id + '/files');
});