7

Próbuję skonfigurować plik API w mojej aplikacji node.js. Moim celem jest możliwość zapisania strumienia plików bezpośrednio do gridfs, bez potrzeby początkowego przechowywania pliku na dysku. Wygląda na to, że działa mój kod tworzenia. Jestem w stanie zapisać plik przesłany do gridfs. Problem polega na czytaniu pliku. Gdy próbuję pobrać zapisany plik za pomocą okna przeglądarki internetowej, widzę, że zawartość pliku są owinięte coś jak następuje:Przesyłanie plików 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-- 

Więc moje pytanie brzmi: co muszę zrobić, aby rozebrać informacje brzegowy ze strumienia plików przed zapisaniem go w gridfs? Oto kod pracuję z:

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

Nawiasem mówiąc, nie jestem obecnie za pomocą dowolnego oprogramowania pośredniczącego dla tych trasach, ale jestem otwarty na tego. Po prostu nie chciałem, aby plik trafił na dysk przed wysłaniem go do gridfs.

Edit:

Per @fardjad dodałem moduł node-multiparty do parsowania wieloczęściowy/form-data i rodzaj pracował. Ale gdy pobieram przesłany plik i porównuję go z oryginałem (jako tekst), istnieje wiele różnic w kodowaniu, a pobrany plik nie zostanie otwarty. Oto moja ostatnia próba.

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

Finał Edit:

Oto prosta implementacja, że ​​skopiowane z innego dewelopera i modyfikowane. Jest to praca dla mnie: (Ja wciąż próbuje dowiedzieć się, dlaczego to nie będzie działać w moim oryginalnym wyraźnej aplikacji Coś wydaje się wtrąca.)

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

Odpowiedz

5

Zobacz mój komentarz w sprawie problemu utworzonego pod numerem github. Miałem ten sam problem, ale udało mi się go rozwiązać. Zawęziłem go do miejsca, w którym byłem przekonany, że problemem było fragment ekspresowego oprogramowania pośredniego, który zmodyfikował to żądanie. Wyłączyłem moje oprogramowanie pośrednie jeden po drugim, aż znalazłem mało prawdopodobnego sprawcy: connect-livereload

Skomentowałem app.use (require ('connect-livereload')()); a problem zniknął. Wierzę, że wstrzykiwał skrypt livereload do odpowiedzi (plik obrazu binarnego).

+0

Ponieważ to właśnie spowodowało mój pierwotny problem, zaznaczam to jako poprawną odpowiedź. –

3

Wygląda na to, że plik ma zostały przesłane za pomocą formularza HTML, w takim przypadku należy dekodować dane kodowane multipart/form-data, w razie potrzeby ponownie je połączyć i zapisać plik na GridFS. Do parsowania można użyć czegoś takiego jak node-multiparty.

+0

Dzięki! Tego właśnie potrzebowałem. Dodałem trochę zaktualizowanego kodu do mojego pytania, które pokazuje, w jaki sposób zintegrowałem węzeł-multiparty. –

+0

Myślę, że mówiłem zbyt szybko. Używając najnowszego kodu, który dodałem powyżej, gdzieś w trakcie wgrywania pliku, a następnie pobierania go, kodowanie pliku się zmienia. Próbuję z plikami PDF i JPEG. Jeśli porównam jako tekst oryginalną kopię do tej, która została przesłana, a następnie pobrana, istnieje wiele różnic między postaciami w ciałach każdego z nich. Jakieś pomysły? –

+0

Zaktualizowałem moje pytanie z działającym kodem. Skończyło się na tym, że używałem [busboy] (https://github.com/mscdex/busboy), ale prawdopodobnie działałoby również wiele osób. –

Powiązane problemy