Я считаю, что если вы хотите сделать POST, вы должны использовать Content-Type: multipart/form-data;boundary=myboundary
заголовок. А потом, в теле, write()
что-то подобное для каждого поля строки (разрывы строк должны быть \r\n
):
--myboundary
Content-Disposition: form-data; name="field_name"
field_value
А потом сам файл, write()
что-то вроде этого к телу:
--myboundary
Content-Disposition: form-data; name="file"; filename="urlencoded_filename.jpg"
Content-Type: image/jpeg
Content-Transfer-Encoding: binary
binary_file_data
binary_file_data
где вы используете pipe()
:
var fileStream = fs.createReadStream("path/to/my/file.jpg");
fileStream.pipe(requestToGoogle, {end: false});
fileStream.on('end, function() {
req.end("--myboundary--\r\n\r\n");
});
{end: false}
предотвращает pipe()
от автоматического закрытия запроса, потому что вам нужно написать еще одну границу после того, как вы закончите отправку файла. Обратите внимание на дополнительные --
на конце границы.
Большая проблема заключается в том, что Google может потребовать заголовок content-length
(весьма вероятно). Если это так, вы не можете передать POST от своего пользователя к POST в Google, потому что вы не сможете достоверно узнать, что такое content-length
, пока вы не получите весь файл.
Значение заголовка content-length
должно быть единственным номером для всего тела. Простой способ сделать это - вызвать Buffer.byteLength(body)
на весь корпус, но это становится уродливым быстро, если у вас большие файлы, а также убивает потоки.В качестве альтернативы можно вычислить его следующим образом:
var body_before_file = "..."; // string fields + boundary and metadata for the file
var body_after_file = "--myboundary--\r\n\r\n";
var fs = require('fs');
fs.stat(local_path_to_file, function(err, file_info) {
var content_length = Buffer.byteLength(body_before_file) +
file_info.size +
Buffer.byteLength(body_after_file);
// create request to google, write content-length and other headers
// write() the body_before_file part,
// and then pipe the file and end the request like we did above
Но, что до сих пор убивает вашу способность течь от пользователя Google, файл должен быть загружен на локальный диск, чтобы определить его длину.
Альтернативный вариант
... теперь, пройдя через все это, PUT может быть вашим другом здесь. Согласно https://developers.google.com/storage/docs/reference-methods#putobject вы можете использовать заголовок transfer-encoding: chunked
, поэтому вам не нужно искать длину файла. И, я считаю, что весь текст запроса является всего лишь файлом, поэтому вы можете использовать pipe()
и просто разрешить ему завершить запрос, когда это будет сделано. Если вы используете https://github.com/felixge/node-formidable для обработки загрузки, то вы можете сделать что-то вроде этого:
incomingForm.onPart = function(part) {
if (part.filename) {
var req = ... // create a PUT request to google and set the headers
part.pipe(req);
} else {
// let formidable handle all non-file parts
incomingForm.handlePart(part);
}
}
К сожалению, Google требует, чтобы быть запрос POST для того, чтобы делать то, что я пытаюсь сделать. Тем не менее, БОЛЬШОЙ ответ, и я, безусловно, буду выполнять сделанные вами предложения. – jwegner
Также, кстати, node.js дает вам возможность [остановить канал от закрытия соединения] (http://nodejs.org/api/stream.html#stream_stream_pipe_destination_options) – jwegner
Хорошо, я забыл об этом. Я собираюсь отредактировать ответ на случай, если кто-нибудь еще столкнется с этим. –