Я считаю, что если вы хотите сделать 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);
}
}