7

Я пытаюсь настроить API-интерфейс файла в приложении node.js. Моя цель - записать поток файлов непосредственно в gridfs, не требуя сначала сохранить файл на диск. Кажется, что мой код создания работает. Я могу сохранить загрузку файла в gridfs. Проблема заключается в чтении файла. Когда я пытаюсь загрузить сохраненный файл через окно веб-браузера, я вижу, что содержимое файла обернуты с чем-то вроде следующего:Загрузка файла Node.js (Express 4, MongoDB, GridFS, GridFS-Stream)

------WebKitFormBoundarye38W9pfG1wiA100l 
Content-Disposition: form-data; name="file"; filename="myfile.txt" 
Content-Type: text/javascript 

***File contents here*** 

------WebKitFormBoundarye38W9pfG1wiA100l-- 

Так что мой вопрос, что мне нужно сделать, чтобы лишить информацию о границе из потока файлов, прежде чем сохранять его в gridfs? Вот код, я работаю с:

'use strict'; 

var mongoose = require('mongoose'); 
var _ = require('lodash'); 

var Grid = require('gridfs-stream'); 
Grid.mongo = mongoose.mongo; 
var gfs = new Grid(mongoose.connection.db); 

// I think this works. I see the file record in fs.files 
exports.create = function(req, res) { 
    var fileId = new mongoose.Types.ObjectId(); 

    var writeStream = gfs.createWriteStream({ 
     _id: fileId, 
     filename: req.query.name, 
     mode: 'w', 
     content_type: req.query.type, 
     metadata: { 
      uploadedBy: req.user._id, 
     } 
    }); 

    writeStream.on('finish', function() { 
     return res.status(200).send({ 
      message: fileId.toString() 
     }); 
    }); 

    req.pipe(writeStream); 
}; 

// File data is returned, but it's wrapped with 
// WebKitFormBoundary and has headers. 
exports.read = function(req, res) { 
    gfs.findOne({ _id: req.params.id }, function (err, file) { 
     if (err) return res.status(400).send(err); 

     // With this commented out, my browser will prompt 
     // me to download the raw file where I can see the 
     // webkit boundary and request headers 
     //res.writeHead(200, { 'Content-Type': file.contentType }); 

     var readstream = gfs.createReadStream({ 
      _id: req.params.id 
      // I also tried this way: 
      //_id: file._id 
     }); 

     readstream.pipe(res); 
    }); 
}; 

Кстати, я не в настоящее время с помощью любого промежуточного программного обеспечения для этих маршрутов, но я открыт для этого. Я просто не хотел, чтобы файл попадал на диск до отправки в gridfs.

Edit:

Per @fardjad, я добавил node-multiparty модуль для разбора многочастных/формы-данных и вида работал. Но когда я загружаю загруженный файл и сравниваю его с оригиналом (как текст), в кодировке много различий, и загруженный файл не открывается. Вот моя последняя попытка.

'use strict'; 

var mongoose = require('mongoose'); 
var _ = require('lodash'); 
var multiparty = require('multiparty'); 
var Grid = require('gridfs-stream'); 
Grid.mongo = mongoose.mongo; 
var gfs = new Grid(mongoose.connection.db); 

exports.create = function(req, res) { 
    var form = new multiparty.Form(); 
    var fileId = new mongoose.Types.ObjectId(); 

    form.on('error', function(err) { 
     console.log('Error parsing form: ' + err.stack); 
    }); 

    form.on('part', function(part) { 
     if (part.filename) { 
      var writeStream = gfs.createWriteStream({ 
       _id: fileId, 
       filename: part.filename, 
       mode: 'w', 
       content_type: part.headers['content-type'], 
       metadata: { 
        uploadedBy: req.user._id, 
       } 
      }) 

      part.pipe(writeStream); 
     } 
    }); 

    // Close emitted after form parsed 
    form.on('close', function() { 
     return res.status(200).send({ 
      message: fileId.toString() 
     }); 
    }); 

    // Parse req 
    form.parse(req); 
}; 

exports.read = function(req, res) { 
    gfs.findOne({ _id: req.params.id }, function (err, file) { 
     if (err) return res.status(400).send(err); 

     res.writeHead(200, { 'Content-Type': file.contentType }); 

     var readstream = gfs.createReadStream({ 
      _id: req.params.id 
     }); 

     readstream.pipe(res); 
    }); 
}; 

Final Edit:

Вот простая реализация, что я скопировал из другого разработчика и модифицирована. Это работает для меня: (Я все еще пытаюсь выяснить, почему он не будет работать в моей первоначальной экспресс-приложение Что-то, кажется, мешая.)

https://gist.github.com/pos1tron/094ac862c9d116096572

var Busboy = require('busboy'); // 0.2.9 
var express = require('express'); // 4.12.3 
var mongo = require('mongodb'); // 2.0.31 
var Grid = require('gridfs-stream'); // 1.1.1" 
var app = express(); 
var server = app.listen(9002); 

var db = new mongo.Db('test', new mongo.Server('127.0.0.1', 27017)); 
var gfs; 
db.open(function(err, db) { 
    if (err) throw err; 
    gfs = Grid(db, mongo); 
}); 

app.post('/file', function(req, res) { 
    var busboy = new Busboy({ headers : req.headers }); 
    var fileId = new mongo.ObjectId(); 

    busboy.on('file', function(fieldname, file, filename, encoding, mimetype) { 
    console.log('got file', filename, mimetype, encoding); 
    var writeStream = gfs.createWriteStream({ 
     _id: fileId, 
     filename: filename, 
     mode: 'w', 
     content_type: mimetype, 
    }); 
    file.pipe(writeStream); 
    }).on('finish', function() { 
    // show a link to the uploaded file 
    res.writeHead(200, {'content-type': 'text/html'}); 
    res.end('<a href="/file/' + fileId.toString() + '">download file</a>'); 
    }); 

    req.pipe(busboy); 
}); 

app.get('/', function(req, res) { 
    // show a file upload form 
    res.writeHead(200, {'content-type': 'text/html'}); 
    res.end(
    '<form action="/file" enctype="multipart/form-data" method="post">'+ 
    '<input type="file" name="file"><br>'+ 
    '<input type="submit" value="Upload">'+ 
    '</form>' 
); 
}); 

app.get('/file/:id', function(req, res) { 
    gfs.findOne({ _id: req.params.id }, function (err, file) { 
    if (err) return res.status(400).send(err); 
    if (!file) return res.status(404).send(''); 

    res.set('Content-Type', file.contentType); 
    res.set('Content-Disposition', 'attachment; filename="' + file.filename + '"'); 

    var readstream = gfs.createReadStream({ 
     _id: file._id 
    }); 

    readstream.on("error", function(err) { 
     console.log("Got error while processing stream " + err.message); 
     res.end(); 
    }); 

    readstream.pipe(res); 
    }); 
}); 

ответ

5

См. Мой комментарий по вопросу, который вы создали на github. У меня была такая же проблема, но мне удалось отладить эту проблему. Я сузил его до того места, где я был уверен, что проблема заключается в том, что часть промежуточного программного обеспечения изменила запрос. Я отключил свое промежуточное ПО по одному, пока не нашел маловероятного виновника: connect-livereload

Я прокомментировал app.use (require ('connect-livereload')()); и проблема исчезла. Я полагаю, что он ввел сценарий печеночной нагрузки в ответ (файл двоичного изображения).

+0

Поскольку это то, что в конечном итоге вызывало мою первоначальную проблему, я отмечаю это как правильный ответ. –

3

Похоже, файл имеет были загружены через HTML-форму, в этом случае вам необходимо декодировать закодированные данные multipart/form-data, повторно собрать детали, если необходимо, и сохранить файл в GridFS. Для синтаксического анализа вы можете использовать что-то вроде node-multiparty.

+0

Спасибо! Это то, что мне нужно. Я добавил к моему вопросу обновленный код, который показывает, как я интегрировал многопартийность узлов. –

+0

Думаю, я говорил слишком рано. Используя последний код, который я добавил выше, где-то в процессе загрузки файла, а затем загружая его, изменяется кодировка файла. Я пытаюсь с PDF и JPEG. Если я сравниваю как текст оригинальную копию с тем, который был загружен, а затем загружен, между персонажами в каждой из тел есть много различий. Есть идеи? –

+0

Я обновил свой вопрос с помощью рабочего кода. Я закончил использовать [busboy] (https://github.com/mscdex/busboy), но, возможно, многопартийный бы работал. –

Смежные вопросы