POST-файл Node.js на сервер - PullRequest
       4

POST-файл Node.js на сервер

5 голосов
/ 30 марта 2012

Я пытаюсь написать приложение, которое позволит моим пользователям загружать файлы в мою учетную запись Google Cloud Storage.Чтобы предотвратить перезапись и выполнить некоторые пользовательские операции по обработке и ведению журнала на моей стороне, я использую сервер Node.js в качестве посредника для загрузки.Итак, процесс выглядит так:

  1. Пользователь загружает файл на сервер Node.js
  2. Сервер Node.js анализирует файл, проверяет тип файла, сохраняет некоторые данные в БД
  3. Сервер Node.js загружает файл в GCS
  4. . Ответ сервера Node.js на запрос пользователя с замечанием о прохождении / отказе

Я немного растерялся на шаге 3, точнокак отправить этот файл в GCS. Этот вопрос дает некоторую полезную информацию, а также хороший пример, но я все еще в замешательстве.

Я понимаю, что могу открыть ReadStream для временного файла загрузки и передать его объекту http.request().Что меня смущает, так это то, как я могу указать в своем запросе POST, что передаваемые данные являются переменной file.В соответствии с GCS API Docs должна быть переменная file, и она должна быть последней.

Итак, как мне указать имя переменной POST дляданные по каналу?

Бонусные баллы, если вы можете сказать мне, как передать их прямо из загрузки моего пользователя, а не хранить во временном файле

1 Ответ

4 голосов
/ 31 марта 2012

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