gridfs - потоковая передача видеофайла больше не работает. Но почему это работало раньше - PullRequest
0 голосов
/ 18 июня 2019

В настоящее время я ожидаю некоторых проблем с бэкендом, которым я управляю. Он использует GridFS для хранения и получения видео в MongoDB. Теперь это работает в течение полугода или около того, но вдруг это уже не так. Это код до сих пор

const getByFilename = async (req: Request, res: Response, next: NextFunction) => {
    try {
        let filename = req.params.Filename
        let media = await MediaService.getByFilename(filename);

        if (req.headers.range) {
            let parts = req.headers.range.replace(/bytes=/, '').split('-');

            let partialStart = parts[0];
            let partialEnd = parts[1];

            let start = parseInt(partialStart, 10);
            let end = partialEnd ? parseInt(partialEnd, 10) : media.length - 1;
            let chunkSize = (end - start) + 1;

            res.writeHead(206, {
                'Accept-Ranges': 'bytes',
                'Content-Length': chunkSize,
                'Content-Range': `bytes ${start}-${end}/${media.length}`,
                'Content-Type': media.contentType,
            });

            let readstream: ReadStream = grid.gfs.createReadStream({
                filename,
                range: {
                    startPos: start,
                    endPos: end
                }
            }).pipe(res);

            readstream.on('data', buff => {
                start += buff.length;
                if (start >= end) {
                    return res.end();
                } else {
                    res.write(buff);
                }
            });

            readstream.on('err', err => {
                handleError(err, res);
            });

            readstream.pipe(res);
        } else {
            res.header('Content-Length', media.length);
            res.header('Content-Type', media.contentType);
            res.header('Content-Metadata', JSON.stringify(media.metadata))

            grid.gfs.createReadStream({
                filename
            }).pipe(res);
        }
    } catch (err) {
        handleError(err, res);
    }
}

Теперь первая ошибка, которую я получаю, заключается в том, что заголовки HTTP пытаются установить после их отправки. Это кажется довольно очевидным (по крайней мере, я предполагаю), потому что код сначала устанавливает переменную readstream путем непосредственного конвейерного подключения, а затем снова конвейерное чтение readstream в конце блока try. Хотя это кажется очевидным, я не могу обернуть голову, как это работало в прошлом (и до сих пор работает в контейнере докера моего продукта). Последний раз gridfs-stream был опубликован 4 года назад, поэтому изменение версии также не является причиной проблемы.

Теперь, если я изменю код, чтобы сначала создать поток чтения (без непосредственного его передачи)

let readstream: ReadStream = grid.gfs.createReadStream({
  filename,
  range: {
    startPos: start,
    endPos: end
  }
});

затем выполняю обработку событий, как до и после того канала, который читает поток

readstream.pipe(res)

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

Есть идеи? Я как бы потерялся здесь

Насколько я понимаю, как это должно работать

  1. Запрос приходит без диапазона в заголовках. и readstream изначально создается и отправляется
  2. Запрос приходит с заголовком диапазона. Приложение будет продолжать запись в буфер, устанавливая заголовок диапазона содержимого в ответе.
  3. Как только запрошенный диапазон достигнет границы запрошенной длины контента, будет отправлен res.end () и ответ будет завершен.

Однако при отладке я замечаю следующие затруднения.

  1. работает как надо
  2. Я начинаю нажимать на часть, где данные записываются в буфер. но не так много раз, как следовало бы.
  3. Иногда срабатывает функция res.end (), иногда нет. Если это происходит, хотя это происходит, и после этого происходит больше шагов 2
...