как загрузить файл из node.js - PullRequest
18 голосов
/ 21 апреля 2011

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

function (data) {
  var reqdata = 'file='+data;
  var request = http.request({
    host : HOST_NAME,
    port : HOST_PORT,
    path : PATH,
    method : 'POST',
    headers : {
      'Content-Type' : 'multipart/form-data',
      'Content-Length' : reqdata.length
    }
  }, function (response) {
      var data = '';
      response.on('data', function(chunk) {
        data += chunk.toString();
      });
      response.on('end', function() {
        console.log(data);
      });
    });

  request.write(reqdata+'\r\n\r\n');
  request.end();
})

Вышеупомянутая функция вызывается другим кодом, который генерирует данные.

Я попытался загрузить тот же файл данных, используя curl -F "file = @ ", и загрузкауспешный.Но мой код не работает.Сервер возвращает специфическую ошибку приложения, которая указывает на то, что загруженный файл был недействительным / поврежденным.

Я собрал данные tcpdump и проанализировал их в wireshark.В пакете, отправленном из моего кода node.js, отсутствует граница, необходимая для составных данных.Я вижу это сообщение в пакете wireshark

The multipart dissector could not find the required boundary parameter.

Есть идеи, как это сделать в коде node.js?

Ответы [ 4 ]

13 голосов
/ 05 апреля 2012

Ответ JHCC почти там.

В наших тестах, чтобы придумать поддержку этого, я немного подправил его.

Вот модифицированная версия, которая работает для нас:

var boundaryKey = Math.random().toString(16); // random string
request.setHeader('Content-Type', 'multipart/form-data; boundary="'+boundaryKey+'"');
// the header for the one and only part (need to use CRLF here)
request.write( 
  '--' + boundaryKey + '\r\n'
  // use your file's mime type here, if known
  + 'Content-Type: application/octet-stream\r\n' 
  // "name" is the name of the form field
  // "filename" is the name of the original file
  + 'Content-Disposition: form-data; name="my_file"; filename="my_file.bin"\r\n'
  + 'Content-Transfer-Encoding: binary\r\n\r\n' 
);
fs.createReadStream('./my_file.bin', { bufferSize: 4 * 1024 })
  .on('end', function() {
    // mark the end of the one and only part
    request.end('\r\n--' + boundaryKey + '--'); 
  })
  // set "end" to false in the options so .end() isn't called on the request
  .pipe(request, { end: false }) // maybe write directly to the socket here?

Изменения:

  • ReadableStream.pipe возвращает поточный канал , поэтому end никогда не вызывается при этом Вместо этого дождитесь end в потоке чтения файла.
  • request.end помещает границу на новую строку.
7 голосов
/ 22 апреля 2011

Multipart довольно сложен, если вы хотите, чтобы клиент выглядел так, как клиент обычно обрабатывает «multipart / form-data», вам нужно сделать несколько вещей.Сначала вы должны выбрать граничный ключ, обычно это случайная строка, чтобы отметить начало и конец частей (в этом случае это будет только одна часть, так как вы хотите отправить один файл).Каждая часть (или одна часть) будет нуждаться в заголовке (инициализированном граничным ключом), задающем тип содержимого, имя поля формы и кодировку передачи.После того, как части завершены, вам нужно пометить конец каждой части граничным ключом.

Я никогда не работал с multipart, но я думаю, что это то, как это можно сделать.Кто-то, пожалуйста, поправьте меня, если я ошибаюсь:

var boundaryKey = Math.random().toString(16); // random string
request.setHeader('Content-Type', 'multipart/form-data; boundary="'+boundaryKey+'"');
// the header for the one and only part (need to use CRLF here)
request.write( 
  '--' + boundaryKey + '\r\n'
  // use your file's mime type here, if known
  + 'Content-Type: application/octet-stream\r\n' 
  // "name" is the name of the form field
  // "filename" is the name of the original file
  + 'Content-Disposition: form-data; name="my_file"; filename="my_file.bin"\r\n'
  + 'Content-Transfer-Encoding: binary\r\n\r\n' 
);
fs.createReadStream('./my_file.bin', { bufferSize: 4 * 1024 })
  // set "end" to false in the options so .end() isnt called on the request
  .pipe(request, { end: false }) // maybe write directly to the socket here?
  .on('end', function() {
    // mark the end of the one and only part
    request.end('--' + boundaryKey + '--'); 
  });

Опять же, я никогда не делал этого раньше, но я думаю , как это можно сделать.Может быть, кто-то более знающий мог бы дать более глубокое понимание.

Если вы хотите отправить его в формате base64 или в кодировке, отличной от необработанного двоичного файла, вы должны будете выполнить все операции конвейера самостоятельно.В конечном итоге все будет сложнее, потому что вам придется приостановить поток чтения и ждать событий утечки по запросу, чтобы убедиться, что вы не используете всю свою память (если это не большой файл, вы обычноне стоит беспокоиться об этом, хотя). EDIT: На самом деле, не важно, что вы могли бы просто установить кодировку в параметрах потока чтения.

Я буду удивлен, если нет модуля Node, который уже делает это.Может быть, кто-то более осведомленный по этому вопросу может помочь с деталями низкого уровня, но я думаю, что где-то должен быть модуль, который делает это.

3 голосов
/ 22 апреля 2011

Поскольку в сообщении об ошибке указано, что вам не хватает граничного параметра.Вам нужно добавить случайную строку, чтобы отделить каждый файл от остальных файлов / данных формы.

Вот как может выглядеть запрос:

Тип содержимого:

Content-Type:multipart/form-data; boundary=----randomstring1337

Тело:

------randomstring1337
Content-Disposition: form-data; name="file"; filename="thefile.txt"
Content-Type: application/octet-stream

[data goes here]

------randomstring1337--

Обратите внимание, что -- в начале и в конце случайной строки в теле является значимым.Это часть протокола.

Подробнее здесь http://www.w3.org/Protocols/rfc1341/7_2_Multipart.html

1 голос
/ 08 июля 2014

Самый быстрый способ, которым я смог это сделать, - это использовать пакет request .Код был хорошо документирован, и он просто работал.

(Для моего тестирования я хотел получить результат JSON и не строгий SSL - есть много других вариантов ...)

var url = "http://"; //you get the idea
var filePath = "/Users/me/Documents/file.csv"; //absolute path created elsewhere
var r = request.post( {
  url: url,
  json: true,
  strictSSL: false
}, function( err, res, data ) {
  //console.log( "Finished uploading a file" );
  expect( err ).to.not.be.ok();
  expect( data ).to.be.ok();
  //callback(); //mine was an async test
} );
var form = r.form();
form.append( 'csv', fs.createReadStream( filePath ) );
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...