MongoError при загрузке файла с использованием mongoose, gridfs-stream и multer
Я использую Express 4, используя multer, gridfs-stream и mongoose с mongodb, и пытаюсь загрузить файл и передать его в 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!");
});
Когда приложение Express запущено, файл выбирается и загружается (через форму 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)
У кого-нибудь есть идеи, что вызывает ошибку и как ее исправить?
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 свойству _id объекта, переданного в gfs.createWriteStream. Оказывается, это вызывает ошибку MongoError в моем коде.
Обнаруженное здесь исправление заключается в изменении строки кода, вызывающей проблему:
_id: fileId.str,
Сделав это, я пришел к вопросу о том, что файл не помещается в коллекцию fs.chunks mongodb, хотя метаданные действительно попадают в коллекцию fs.files mongodb. Ответ AddieD здесь начинает адресовать это:)
Проблема заключается в том, что вы не просто посылаете запрос файла в gridfs-stream. Использование Multer в качестве промежуточного программного обеспечения с catch любой multipart/form-data
отправить запрос. Когда поток приходит, Малтер (построенный на Busboy) наблюдает за on('field')
а также on('file')
события и разборы соответственно. То, что вы посылаете Малтеру, - это не просто файл.
Этот кусок кода работает нормально, потому что Малтер действительно разобрался за вас 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);
Мне еще предстоит найти способ для потоковой передачи многопартийного / форм-запроса данных с помощью не просто загрузки файлов прямо в GridFS. Если ваш пост-запрос не содержит ничего, кроме файла (то есть никаких других полей ввода html в форме), то вы можете рассмотреть возможность удаления Multer из промежуточного программного обеспечения, по крайней мере, для этого маршрута.
Для моего случая использования мне требуется возможность получать html-форму с текстовым вводом вместе с загрузкой файла (я сохраняю метаданные о файле при загрузке). Вот как я выполнил то, что мне было нужно с Малтером:
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 в качестве промежуточного программного обеспечения только на маршрутах, которые в этом нуждаются. Вы можете прочитать больше об этой мысли в последнем разделе этого.
Обновить
Я думаю, что я близок к тому, чтобы найти способ потоковой передачи напрямую в GridFS с метаданными с помощью Skipper. Я нахожусь в процессе выяснения, не могу ли я получить некоторые обновления, идущие к Skipper-gridfs, которые будут принимать текстовые вводы в HTML-форме и устанавливать их как метаданные и т. Д. Настройка skipper-gridfs кажется довольно незначительной. Я обновлю, когда это будет сброшено. Если вы посмотрите на шкипера, убедитесь, что вы понимаете порядок ввода html-формы (если он у вас есть) имеет значение.