在Node.js中用MongoDB删除上传的文件

我有两个实体之间的一对多关系:课程和文件。

所以,我设置我的数据库的方式是使用两个独立的集合:课程和文件。

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

我有一个工作页面,允许用户在课程中上传和添加文件。 同样,他们也可以在同一页面上删除选定的文件。

我担心的是当我删除选定的文件。 select需要删除的文件并点击提交button后,我发送一个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,从实际的集合中删除文件,并从文件系统中删除文件。 另外,在每个步骤的开始,我都有一些error handling代码。

这可以改善使用更less的步骤和/或更好的error handling?

我会这样写,更清洁imho,使用eachwaterfall ,迭代收集和通过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'); }); } 

另一个select是使用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'); }); 
Interesting Posts