我正在使用multer,gridfs-stream和mongoose使用mongodb运行express 4,我正在尝试上传文件并将其流式传输到gridfs。
执行此操作的快速路线定义为:
app.post('/uploadfile', function (req, res) {
console.dir(req.files);
// The mongodb instance created when the mongoose.connection is opened
var db = mongoose.connection.db;
// The native mongo driver which is used by mongoose
var mongoDriver = mongoose.mongo;
// Create a gridfs-stream
var gfs = new Gridfs(db, mongoDriver);
var file = req.files.myFile;
var fileId = new ObjectId();
console.log("Creating WriteStream");
var writeStream = gfs.createWriteStream({
_id: fileId,
filename: file.originalname,
mode: 'w',
content_type: file.mimetype,
metadata: {
id: '123',
number: '2',
name: "Kenny Erasmuson"
}
});
console.log("Created WriteStream");
req.pipe(writeStream);
console.log("Finished!");
});
快递应用程序运行后,会选择并上传文件(通过HTML multipart / form-data表单),节点服务器的输出为:
$ node server.js
Listening on port 8001
{ myFile:
{ fieldname: 'myFile',
originalname: 'kenny-credit-rating.pdf',
name: '1082e5071ede1002c4ae5be6123226d8.pdf',
encoding: '7bit',
mimetype: 'application/pdf',
path: 'uploads/1082e5071ede1002c4ae5be6123226d8.pdf',
extension: 'pdf',
size: 110782,
truncated: false,
buffer: null }
}
Creating WriteStream
Created WriteStream
Finished!
/Users/kenny/Dropbox/Dev/fileUploader/node_modules/mongoose/node_modules/mongodb/lib/mongodb/co nnection/base.js:246
throw message;
^
MongoError: The dollar ($) prefixed field '$conditionalHandlers' in '_id.$conditionalHandlers' is not valid for storage.
at Object.toError (/Users/kenny/Dropbox/Dev/fileUploader/node_modules/mongoose/node_modules/mongodb/lib/mongodb/utils.js:114:11)
at /Users/kenny/Dropbox/Dev/fileUploader/node_modules/mongoose/node_modules/mongodb/lib/mongodb/collection/core.js:569:27
at /Users/kenny/Dropbox/Dev/fileUploader/node_modules/mongoose/node_modules/mongodb/lib/mongodb/db.js:1157:7
at /Users/kenny/Dropbox/Dev/fileUploader/node_modules/mongoose/node_modules/mongodb/lib/mongodb/db.js:1890:9
at Server.Base._callHandler (/Users/kenny/Dropbox/Dev/fileUploader/node_modules/mongoose/node_modules/mongodb/lib/mongodb/connection/base.js:448:41)
at /Users/kenny/Dropbox/Dev/fileUploader/node_modules/mongoose/node_modules/mongodb/lib/mongodb/connection/server.js:481:18
at MongoReply.parseBody (/Users/kenny/Dropbox/Dev/fileUploader/node_modules/mongoose/node_modules/mongodb/lib/mongodb/responses/mongo_reply.js:68:5)
at null.<anonymous> (/Users/kenny/Dropbox/Dev/fileUploader/node_modules/mongoose/node_modules/mongodb/lib/mongodb/connection/server.js:439:20)
at emit (events.js:95:17)
at null.<anonymous> (/Users/kenny/Dropbox/Dev/fileUploader/node_modules/mongoose/node_modules/mongodb/lib/mongodb/connection/connection_pool.js:201:13)
有没有人知道导致错误的原因以及如何修复?
答案 0 :(得分:5)
问题在于您没有将文件请求仅传递给gridfs-stream。使用Multer作为中间件,捕获任何multipart/form-data
帖子请求。当流进来时,Multer(建立在Busboy上)监视on('field')
和on('file')
事件,并进行相应的解析。你对Multer的管道不仅仅是一个档案。
这段代码运行正常,因为此时Multer确实为您解析了req.files
和req.body
:
// Create a gridfs-stream
var gfs = new Gridfs(db, mongoDriver);
var file = req.files.myFile;
var fileId = new ObjectId();
console.log("Creating WriteStream");
var writeStream = gfs.createWriteStream({
_id: fileId,
filename: file.originalname,
mode: 'w',
content_type: file.mimetype,
metadata: {
id: '123',
number: '2',
name: "Kenny Erasmuson"
}
});
console.log("Created WriteStream");
但是你的代码遇到问题的地方出于上述原因:
req.pipe(writeStream);
我还没有找到一种方法来流式传输multipart / form-data post请求,而不仅仅是直接将文件上传到GridFS。如果您的发布请求不包含文件以外的任何内容(表示表单中没有其他html输入字段),那么您可能需要考虑至少为此路由从中间件中删除Multer。
对于我的用例,我需要能够接收带有文本输入和文件上传的html表单帖子(我在上传时存储有关文件的元数据)。以下是我用Multer完成所需的工作:
var uploadImg = function(req,res) {
var writestream = gfs.createWriteStream({
filename: req.files.file.name,
mode:'w',
content_type:req.files.file.mimetype,
metadata:req.body,
});
fs.createReadStream(req.files.file.path).pipe(writestream);
writestream.on('close', function (file) {
res.send("Success!");
fs.unlink(req.files.file.path, function (err) {
if (err) console.error("Error: " + err);
console.log('successfully deleted : '+ req.files.file.path );
});
});
};
默认情况下,Multer会将您的文件存储在磁盘上。一个快速的解决方案是创建一个读取流并将其直接传输回GridFS。写入完成后,删除tmp文件。
作为旁注,似乎有一些想法,最好只在需要它的路线上使用Multer作为中间件。您可以在this的最后一部分阅读有关该想法的更多信息。
我认为我已经找到了一种使用Skipper直接使用元数据直接传输到GridFS 的方法。我正在查看我是否无法获得更新skipper-gridfs的更新,这些更新将采用html表单上的文本输入并将其设置为元数据等。对于skipper-gridfs的调整看起来很漂亮次要。当它被刷新时我会更新。如果你看船长,请确保你认识到html表单输入的顺序(如果你有的话)确实很重要。
答案 1 :(得分:2)
在我的代码中:
var fileId = new ObjectId();
console.log("Creating WriteStream");
var writeStream = gfs.createWriteStream({
_id: fileId,
filename: file.originalname,
mode: 'w',
content_type: file.mimetype,
metadata: {
id: '123',
number: '2',
name: "Kenny Erasmuson"
}
});
我将ObjectId而不是ObjectId的字符串表示赋给传递给gfs.createWriteStream的对象的_id属性。事实证明这导致了我的代码中的MongoError。
发现here的修补程序是更改导致问题的代码行:
_id: fileId.str,
完成后我发现文件没有放入fs.chunks mongodb集合,尽管元数据确实最终出现在fs.files mongodb集合中。 AddieD的回答here开始解决这个问题:)