7

Ich versuche, eine Datei-API in meiner node.js-Anwendung einzurichten. Mein Ziel ist es, in der Lage zu sein, den Dateistrom direkt in gridfs zu schreiben, ohne die Datei zunächst auf dem Datenträger speichern zu müssen. Es scheint so, als würde mein Erstellungscode funktionieren. Ich kann einen Datei-Upload in Gridfs speichern. Das Problem ist das Lesen der Datei. Wenn ich versuche, eine gespeicherte Datei über ein Web-Browser-Fenster zum Download, sehe ich, dass der Inhalt der Datei mit etwas eingewickelt werden wie folgt aus:Node.js Datei-Upload (Express 4, MongoDB, GridFS, GridFS-Stream)

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

***File contents here*** 

------WebKitFormBoundarye38W9pfG1wiA100l-- 

So ist meine Frage, was muss ich tun, um die Grenzinformationen strippen aus dem Dateistream vor dem Speichern in gridfs? Hier ist der Code, den ich mit gerade arbeite:

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

By the way, ich bin derzeit nicht unter Verwendung eines beliebigen Middleware für diese Strecken, aber ich bin offen für so tun. Ich wollte einfach nicht, dass die Datei auf die Festplatte trifft, bevor sie an Gridfs gesendet wird.

Edit:

Per @fardjad, habe ich das node-multiparty Modul für multipart/form-data-Parsing und es ist eine Art gearbeitet. Aber wenn ich eine hochgeladene Datei herunterlade und mit einem Original (als Text) vergleiche, gibt es viele Unterschiede in der Kodierung, und die heruntergeladene Datei wird nicht geöffnet. Hier ist mein letzter Versuch.

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

Finale Edit:

Hier ist eine einfache Implementierung, die ich von einem anderen Entwickler kopiert und modifiziert. Dies funktioniert für mich: (Ich bin immer noch versuchen, herauszufinden, warum es nicht in meinem ursprünglichen Express App funktioniert Etwas scheint zu stören werden.)

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

Antwort

5

Siehe meinen Kommentar zu dem Problem, das Sie unter github erstellt haben. Ich hatte das gleiche Problem, aber ich konnte das Problem beheben. Ich verengte mich darauf, wo ich sicher war, dass das Problem ein Stück Express-Middleware war, das die Anfrage modifiziert hatte. Ich habe meine Middleware nacheinander deaktiviert, bis ich den unwahrscheinlichen Täter gefunden habe: connect-livreload

Ich habe auskommentiert app.use (require ('connect-livreload')()); und das Problem ging weg. Ich glaube, es war die Injektion des Liveload-Skript in die Antwort (eine binäre Bilddatei).

+0

Weil dies letztendlich mein ursprüngliches Problem verursacht hat, markiere ich dies als die richtige Antwort. –

3

Sieht aus wie die Datei hat wurde über ein HTML-Formular hochgeladen, in diesem Fall müssen Sie die codierten Daten multipart/form-data decodieren, die Teile bei Bedarf erneut zusammenbauen und die Datei unter GridFS speichern. Für das Parsen können Sie etwas wie node-multiparty verwenden.

+0

Danke! Das habe ich gebraucht. Ich habe meiner Frage einen aktualisierten Code hinzugefügt, der zeigt, wie ich Knoten-Multiparty integriert habe. –

+0

Ich glaube, ich habe zu früh gesprochen. Unter Verwendung des neuesten Codes, den ich oben hinzugefügt habe, irgendwo während des Hochladens der Datei und dem anschließenden Herunterladen ändert sich die Dateicodierung. Ich versuche es mit PDFs und JPEGs. Wenn ich als Text eine Originalkopie mit einer Kopie vergleiche, die hochgeladen und dann heruntergeladen wurde, gibt es viele Unterschiede zwischen den Charakteren in den Körpern der einzelnen. Irgendwelche Ideen? –

+0

Ich habe meine Frage mit Arbeitscode aktualisiert. Am Ende habe ich [busboy] (https://github.com/mscdex/busboy) benutzt, aber wahrscheinlich hätte auch Multiparty funktioniert. –