Подтвердить что ты не робот

Как загрузить файл из node.js

Я нашел много сообщений, когда я запросил эту проблему, но все они относятся к тому, как загрузить файл из вашего браузера на сервер 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 = @<filepath> " и загрузка выполнена успешно. Но мой код не работает. Сервер возвращает конкретную ошибку приложения, которая указывает, что загруженный файл недействителен/поврежден.

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

The multipart dissector could not find the required boundary parameter.

Любая идея, как это сделать в node.js-коде?

4b9b3361

Ответ 1

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 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 или кодировку, отличную от raw-двоичной, вам придется делать все сами трубопроводы. Это будет сложнее, потому что вам придется приостанавливать поток чтения и ждать событий утечки в запросе, чтобы убедиться, что вы не используете всю свою память (если это не большой файл, вы, как правило, не придется беспокоиться об этом, хотя). EDIT: Собственно, неважно, что вы можете просто установить кодировку в параметрах потока чтения.

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

Ответ 2

jhcc answer почти есть.

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

Здесь измененная версия, которая работает для нас:

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 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?

Изменения:

Ответ 3

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

Вот как выглядит запрос:

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

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

Ответ 4

Самый быстрый способ, которым я смог это сделать, работал, использовал пакет 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 ) );