Node.js и Request - ограничение размера файла загруженного файла

Я хочу загрузить файл с помощью библиотеки Request. Это довольно просто:

request({
    url: url-to-file
}).pipe(fs.createWriteStream(file));

Поскольку URL-адрес предоставляется пользователями (в моем случае), я хотел бы ограничить максимальный размер файла, которое загрузит мое приложение - пусть говорят 10 МБ. Я мог бы полагаться на заголовки content-length следующим образом:

request({
    url: url-to-file
}, function (err, res, body) {
    var size = parseInt(res.headers['content-length'], 10);

    if (size > 10485760) {
        // ooops - file size too large
    }
}).pipe(fs.createWriteStream(file));

Вопрос в том, насколько это надежным? Думаю, этот обратный вызов будет вызван после того, как файл будет загружен, верно? Но это слишком поздно, если кто-то поставляет URL-адрес файла размером 1 ГБ. Мое приложение сначала загрузит этот 1 ГБ файла, чтобы проверить (в обратном вызове), что он слишком большой.

Я тоже думал о добром старом Node http.get() методе. В этом случае я бы сделал следующее:

var opts = {
    host: host,
    port: port,
    path: path
};

var file = fs.createWriteStream(fileName),
    fileLength = 0;

http.get(opts, function (res) {
    res.on('data', function (chunk) {
        fileLength += chunk.length;

        if (fileLength > 10485760) { // ooops - file size too large
            file.end();
            return res.end();
        }

        file.write(chunk);
    }).on('end', function () {
        file.end();
    });
});

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

Ответ 1

Я бы использовал оба метода, которые вы обсуждали: проверьте заголовок content-legnth и посмотрите поток данных, чтобы убедиться, что он не превышает ваш лимит.

Чтобы сделать это, я сначала сделаю запрос HEAD к URL-адресу, чтобы узнать, доступен ли заголовок content-length. Если он больше вашего предела, вы можете остановиться прямо там. Если он не существует или он меньше вашего предела, сделайте фактический запрос GET. Поскольку запрос HEAD будет возвращать заголовки и фактический контент, это поможет быстро избавиться от больших файлов с действующим content-length.

Затем сделайте фактический запрос GET и посмотрите размер входящих данных, чтобы убедиться, что он не превышает ваш лимит (это можно сделать с помощью модуля запроса, см. ниже). Вы захотите сделать это независимо от того, нашел ли запрос HEAD заголовок content-length, как проверку работоспособности (сервер может лежать о content-length).

Что-то вроде этого:

var maxSize = 10485760;

request({
    url: url,
    method: "HEAD"
}, function(err, headRes) {
    var size = headRes.headers['content-length'];
    if (size > maxSize) {
        console.log('Resource size exceeds limit (' + size + ')');
    } else {
        var file = fs.createWriteStream(filename),
            size = 0;

        var res = request({ url: url });

        res.on('data', function(data) {
            size += data.length;

            if (size > maxSize) {
                console.log('Resource stream exceeded limit (' + size + ')');

                res.abort(); // Abort the response (close and cleanup the stream)
                fs.unlink(filename); // Delete the file we were downloading the data to
            }
        }).pipe(file);
    }
});

Трюк для наблюдения за размером входящих данных с помощью модуля запроса заключается в привязке к событию data в ответе (например, вы думали о том, как это сделать с использованием модуля http), прежде чем вы начнете связывать его с файловым потоком, Если размер данных превышает максимальный размер файла, вызовите метод ответа abort().