MongoError при загрузке файла с использованием mongoose, gridfs-stream и multer

Я запускаю экспресс 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!");
});

После запуска экспресс-приложения файл выбирается и загружается (через 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)

У кого-нибудь есть идеи, что вызывает ошибку и как ее исправить?


person KennyE    schedule 26.11.2014    source источник


Ответы (2)


Проблема заключается в том, что вы не просто передаете запрос файла в gridfs-stream. Использование Multer в качестве промежуточного ПО для перехвата любого multipart/form-data почтового запроса. По мере поступления потока Multer (созданный на основе Busboy) отслеживает события on('field') и on('file') и анализирует их соответствующим образом. То, что вы передаете Мултеру, — это не просто файл.

Этот фрагмент кода работает нормально, потому что 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 с более чем простой загрузкой файлов прямо в 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 в качестве промежуточного программного обеспечения только на тех маршрутах, которые в нем нуждаются. Вы можете прочитать больше об этой мысли в последнем разделе этого.

Обновлять

Я думаю, что я близок к тому, чтобы найти способ прямой потоковой передачи с метаданными GridFS с помощью Skipper. . Я сейчас выясняю, не могу ли я получить некоторые обновления для skipper-gridfs. который будет принимать текстовые входы в форме html и устанавливать их как метаданные и т. д. Настройка skipper-gridfs кажется довольно незначительной. Я обновлю, когда это будет смыто. Если вы посмотрите на шкипера, убедитесь, что вы понимаете, что порядок ввода вашей html-формы (если он у вас есть) имеет значение.

person AddieD    schedule 27.11.2014
comment
В случае, если кому-то интересно, я придумал быстрое решение с помощью форка skipper-gridfs. Изменения в репозитории были незначительными и могли бы выдержать некоторую очистку, но у меня он отлично работает, и я, вероятно, скоро буду использовать его в производстве здесь. Здесь есть запрос на вытягивание -- https://github.com/willhuang85/skipper-gridfs/pull/9 Теперь я выполняю потоковую передачу прямо в gridfs — с метаданными из формы — без необходимости сначала сохранять файл. Отлично работает. - person AddieD; 29.11.2014

В моем коде:

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 здесь начинает решать эту проблему :)

person KennyE    schedule 27.11.2014