Hochladen der Datei mithilfe der POST-Anforderung in Node.js.

76

Ich habe Probleme beim Hochladen der Datei mithilfe der POST-Anforderung in Node.js. Ich muss ein requestModul verwenden, um dies zu erreichen (keine externen npms). Der Server muss eine mehrteilige Anforderung mit dem fileFeld sein, das die Daten der Datei enthält. Was einfach zu sein scheint, ist in Node.js ziemlich schwierig, ohne ein externes Modul zu verwenden.

Ich habe versucht, dieses Beispiel zu verwenden, aber ohne Erfolg:

request.post({
  uri: url,
  method: 'POST',
  multipart: [{
    body: '<FILE_DATA>'
  }]
}, function (err, resp, body) {
  if (err) {
    console.log('Error!');
  } else {
    console.log('URL: ' + body);
  }
});
Łukasz Jagodziński
quelle
1
Haben Sie Ihr Formular mit Option enctype="multipart/form-data"?
Monkeyinsight
2
Ich benutze kein Formular. Es ist eine Serveranfrage. Ich sende eine Datei vom Browser über Sockets an den Server und später muss ich diese Datei per POST-Anfrage an einen anderen Server senden.
Łukasz Jagodziński

Antworten:

114

Sieht so aus, als würden Sie bereits ein requestModul verwenden .

In diesem Fall müssen Sie lediglich multipart/form-datadie folgende formFunktion verwenden :

var req = request.post(url, function (err, resp, body) {
  if (err) {
    console.log('Error!');
  } else {
    console.log('URL: ' + body);
  }
});
var form = req.form();
form.append('file', '<FILE_DATA>', {
  filename: 'myfile.txt',
  contentType: 'text/plain'
});

Wenn Sie jedoch eine vorhandene Datei aus Ihrem Dateisystem veröffentlichen möchten, können Sie sie einfach als lesbaren Stream übergeben:

form.append('file', fs.createReadStream(filepath));

request extrahiert alle zugehörigen Metadaten selbst.

Weitere Informationen zum Posten finden multipart/form-dataSie im node-form-dataModul , das intern von verwendet wird request.

Leonid Beschastny
quelle
83
Als ich den Knoten und das Anforderungsmodul lernte, war ich verwirrt, warum das Formular nach dem postAufruf der Methode geändert werden konnte. In den Anforderungsdokumenten ist die Erklärung vergraben - das Formular " kann geändert werden, bis die Anforderung im nächsten Zyklus der Ereignisschleife ausgelöst wird ".
Doug Donohoe
3
Ich bekomme immer wieder '[Fehler: Schreiben nach Ende]', wenn ich form und form.append verwende. Weiß jemand warum?
Vitor Freitas
1
@VitorFreitas sollten Sie req.form()direkt nach dem Aufruf aufrufen und synchron mit allen entsprechenden Daten füllen request.post. Es ist wichtig, dies während des gleichen Ereignisschleifen-Ticks zu tun, da Ihre Anfrage sonst möglicherweise bereits gesendet und der zugrunde liegende Stream geschlossen wird.
Leonid Beschastny
@LeonidBeschastny Kannst du einen Blick in meinen Code werfen? pastebin.com/E6b0cvag . Es scheint mir richtig, danke! requestService ist das Anforderungsmodul
Vitor Freitas
1
Die Anfrage war veraltet. Haben Sie eine Alternative?
David
20

Eine undokumentierte Funktion des implementierten formDataFelds requestist die Möglichkeit, Optionen an das verwendete form-dataModul zu übergeben:

request({
  url: 'http://example.com',
  method: 'POST',
  formData: {
    'regularField': 'someValue',
    'regularFile': someFileStream,
    'customBufferFile': {
      value: fileBufferData,
      options: {
        filename: 'myfile.bin'
      }
    }
  }
}, handleResponse);

Dies ist nützlich, wenn Sie einen Anruf vermeiden, requestObj.form()aber einen Puffer als Datei hochladen müssen. Das form-dataModul akzeptiert auch contentType(den MIME-Typ) und knownLengthOptionen.

Diese Änderung wurde im Oktober 2014 hinzugefügt (also 2 Monate nachdem diese Frage gestellt wurde), daher sollte sie jetzt sicher verwendet werden können (ab 2017). Dies entspricht der Version v2.46.0oder höher von request.

Clavin
quelle
4

Die Antwort von Leonid Beschastny funktioniert, aber ich musste auch ArrayBuffer in Buffer konvertieren, der im requestModul des Knotens verwendet wird. Nach dem Hochladen der Datei auf den Server hatte ich sie im gleichen Format wie die HTML5 FileAPI (ich verwende Meteor). Vollständiger Code unten - vielleicht ist er für andere hilfreich.

function toBuffer(ab) {
  var buffer = new Buffer(ab.byteLength);
  var view = new Uint8Array(ab);
  for (var i = 0; i < buffer.length; ++i) {
    buffer[i] = view[i];
  }
  return buffer;
}

var req = request.post(url, function (err, resp, body) {
  if (err) {
    console.log('Error!');
  } else {
    console.log('URL: ' + body);
  }
});
var form = req.form();
form.append('file', toBuffer(file.data), {
  filename: file.name,
  contentType: file.type
});
Łukasz Jagodziński
quelle
4
Es ist eine einfachere Art und Weise zu konvertieren ArrayBufferzu Buffer, mit build-in - Buffer Konstruktor aus einem Array von Bytes :var buffer = new Buffer(new Uint8Array(ab));
Leonid Beschastny
2
Woher stammt die "Datei" in file.data, file.name und file.type in Ihrer letzten Funktion? Ich sehe diese Variable nirgendwo anders erwähnt.
MichaelAdam
Ich verwende Meteor und Community-Paket für die Dateiverwaltung. Wenn Sie jedoch einen reinen Knoten verwenden, können Sie Dateisystemfunktionen verwenden, um alle Informationen über die Datei und ihre Daten abzurufen. Nodejs.org/api/fs.html
Łukasz Jagodziński
4

Sie können auch die Unterstützung für "benutzerdefinierte Optionen" aus der Anforderungsbibliothek verwenden. Mit diesem Format können Sie einen mehrteiligen Formular-Upload erstellen, jedoch mit einem kombinierten Eintrag sowohl für die Datei als auch für zusätzliche Formularinformationen wie Dateiname oder Inhaltstyp. Ich habe festgestellt, dass einige Bibliotheken erwarten, dass Dateien in diesem Format hochgeladen werden, insbesondere Bibliotheken wie multer.

Dieser Ansatz ist offiziell im Formularbereich der Anforderungsdokumente unter https://github.com/request/request#forms dokumentiert

//toUpload is the name of the input file: <input type="file" name="toUpload">

let fileToUpload = req.file;

let formData = {
    toUpload: {
      value: fs.createReadStream(path.join(__dirname, '..', '..','upload', fileToUpload.filename)),
      options: {
        filename: fileToUpload.originalname,
        contentType: fileToUpload.mimeType
      }
    }
  };
let options = {
    url: url,
    method: 'POST',
    formData: formData
  }
request(options, function (err, resp, body) {
    if (err)
      cb(err);

    if (!err && resp.statusCode == 200) {
      cb(null, body);
    }
  });
Marwen Landoulsi
quelle
4
Bitte bearbeiten Sie Ihre Antwort und fügen Sie eine Erklärung oder einen Kommentar zur Funktionsweise Ihres Codes hinzu. Dies würde anderen Benutzern helfen, zu entscheiden, ob Ihre Antwort interessant genug ist, um berücksichtigt zu werden. Andernfalls müssen die Benutzer Ihren Code analysieren (was einige Zeit in Anspruch nimmt), um eine vage Vorstellung davon zu haben, ob dies das ist, was sie benötigen. Vielen Dank!
Fabio sagt Reinstate Monica
5 Jahre später wird jemand eine Erklärung wollen und Sie werden nicht da sein oder sich nicht darum kümmern. Deshalb hat Fabio Sie gebeten, die Erklärung in die Antwort aufzunehmen und nicht auf Anfrage.
user985366
0
 const remoteReq = request({
    method: 'POST',
    uri: 'http://host.com/api/upload',
    headers: {
      'Authorization': 'Bearer ' + req.query.token,
      'Content-Type': req.headers['content-type'] || 'multipart/form-data;'
    }
  })
  req.pipe(remoteReq);
  remoteReq.pipe(res);
Xu Xiang
quelle