Как повторить запрос, основанный на заголовках ответа, используя request и nodejs?

У меня есть небольшой скрипт nodejs, и я хочу скачать zip-файл:

const fs = requrie("fs");
const request = require("requestretry");

export class FileFetcher {
    public async fetchFile(url: string): Promise<string> {
        const fileName = "./download.zip";
        return new Promise<string>(resolve => {
            request(url)
                .pipe(fs.createWriteStream(fileName))
                .on("close", function () {
                    resolve(fileName);
                });
        });
    }
}

Я использую requestretry библиотека, обертка вокруг request, поскольку файл может существовать только в будущем и, следовательно, скорее всего, потерпит неудачу в первые пару раз.

И все же я должен приспособиться к своей странной внешности конечной точки. Это всегда возвращает 200 OK вместо ожидаемого 404 Not Found,

Следовательно, единственный способ определить это Content-Length Заголовок в ответе. Если Content-Length: 0 тогда нет файла.

Кажется, есть разные стратегии повторения в requestretry Тем не менее, все они, кажется, принимают хорошо сложенный API, например, request.RetryStrategies.HTTPOrNetworkError, Поскольку он получает 200, он никогда не будет повторять попытку и "успешно" загрузить пустой zip-файл.

Я не зациклен на использовании requestretry Мне интересно, как повторить запрос на основе заголовков ответа.

2 ответа

Вы можете определить свою собственную логику повтора с помощью retryStrategy, например:

request({
  url: '...',
  retryStrategy: function(err, res, body) {
    return res.headers['Content-Length'] === 0;
  }
});

Я бросил requestretry библиотека и пошла с request и сам реализовал механизм повтора.

Одной из проблем было получение фактических данных тела ответа. Я ожидал, что это будет доступно через response событие, но это только заголовок.

Данные могут быть получены с помощью нескольких событий данных и разрешения обещания конечного события.

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

Мой класс загрузчика выглядит так:

export class FileFetcher {
    public async downloadWithRetry(url: string, maxRetries: number, timeout: number): Promise<Buffer> {
        while (true) {
            try {
                const buffer = await this.fetchFile(url);
                return new Promise<Buffer>(resolve => {
                    resolve(buffer);
                });
            } catch (e) {
                maxRetries = maxRetries - 1;

                if (maxRetries <= 0) {
                    throw new Error("Too many requests");
                }

                console.warn(`No file at url:${url}, waiting ...`);
                await this.wait(timeout);
                console.log(`Continue.`);
            }
        }
    }

    private async wait(timeout: number): Promise<any> {
        timeout *= 1e3;
        console.log(timeout);
        return new Promise(resolve => {
            setTimeout(resolve, timeout);
        });
    }

    private async fetchFile(url: string): Promise <Buffer> {
        return new Promise<Buffer>((resolve, reject) => {
            let data = [];
            request.get({
                encoding: null,
                url: url,
            }).on("data", function (chunk) {
                data.push(chunk);
            }).on("response", function (response) {
                /**
                 * Server always returns 200 OK even if file does not exist yet. Hence checking for content-lenth head
                 */
                if (!(response.headers["content-length"] > 0)) {
                    reject("Empty response!");
                }
            }).on("end", function () {
                const body = Buffer.concat(data);
                if (body.length > 0) {
                    resolve(body);
                    return;
                }

                reject("Empty response");
            });
        });
    }
}

Этот буфер может быть записан через fs.writeFile(file, buffer),

Даунсайд больше не использует потоковый подход, так как я хотел передать данные. Тем не менее, этот подход, по крайней мере, правильно выбирает файл.

Другие вопросы по тегам