express. js и request. js - неполная передача PDF при использовании синтаксиса обратного вызова - PullRequest
1 голос
/ 13 апреля 2020

Упрощенный вопрос

почему при использовании express. js & request. js следующие два примера:

request.get(url)
.on('response' (requestjsResponse) => {
  requestjsResponse.pipe(res);
})

и

request.get(url, (err, requestjsResponse, requestjsBody) => {
  res.send(requestjsResponse)
})

Не дает одинаковых результатов, даже если requestjsBody содержит ожидаемое содержимое?

Подробный вопрос

У меня есть две express.js версии обработчика маршрута, которые обрабатывают некоторые процедуры проксирования файлов для нескольких типы файлов. Код использует стандартные обозначения express. js req/res/next. В основном, что может быть важно из фона, информация, не связанная с кодом, для этой проблемы заключается в том, что два наиболее возвращаемых типа обрабатываются следующим образом:

  • PDF: должны открываться в браузере, их размер обычно равен не менее 18 КБ (согласно заголовку длины содержимого)
  • EML: загружается, размер их обычно меньше 16 КБ (согласно заголовку длины содержимого)

Оба обработчика версии используют request.js, одну с

get(url: string, callback: (Error, Response, Body) => void)

формой, которую я буду называть формой обратного вызова , где внутри такого обратного вызова ожидается все тело . В этом случае ответ пользователю отправляется простым текстом express. js res.send(Body). другой использует форму

get(url: string).on(event: 'response', callback: listener: (request.Response) => void)

, которую я буду называть формой event / pipe , и передает ответ конечному пользователю, передавая его по request.Response.pipe(res) внутри обработчика ответа. Подробности приведены в листинге кода.

Я не могу найти разницу между этими двумя формами, но: В случае .eml (MIME message / rfc822, вы можете угрожать им как причудливыми HTML) файлами версии работают точно так же, файл хорошо загружен.

В случае .pdf, при использовании формы события / канала get(url).on('response', callback) Я могу успешно передать PDF-документ клиенту. Когда я использую форму обратного вызова (то есть get(url: string, callback: (Error, Response, Body) => void)), даже когда я просматриваю тело в отладчике (кажется, что это полный PDF, содержит заголовок PDF, маркер EOF, например, c .t.), Клиент получает только какая-то странная преамбула, объявляющая HTML:

<!doctype html><html><body style='height: 100%; width: 100%; overflow: hidden; margin:0px; background-color: rgb(82, 86, 89);'><embed style='position:absolute; left: 0; top: 0;'width='100%' height='100%' src='about:blank' type='application/pdf' internalid='FD93AFE96F19F67BE0799686C52D978F'></embed></body></html>

, но впоследствии документ PDF не был получен. Chrome утверждает, что ему не удалось загрузить документ.

Пожалуйста, смотрите код:

Не работающая версия обратного вызова:

request.get(url, (err, documentResponse, documentBody) => {
    if (err) {
        logger.error('Document Fetch error:');
        logger.error(err);
    } else {
        const documentResponseContentLength = Number.parseInt(documentResponse.headers['content-length'], 10);
        if (documentResponseContentLength === 0 || Number.isNaN(documentResponseContentLength)) {
            logger.warn('No content provided for requested document or length header malformed');
            res.redirect(get404Navigation());
        }
        if (mimetype === 'application/pdf') {
            logger.info('   overwriting Headers (PDF)');
            res.set('content-type', 'application/pdf');
            // eslint-disable-next-line max-len, prefer-template
            res.set('content-disposition', 'inline; filename="someName.pdf"');
            logger.info('Document Download Headers (overridden):', res.headers);
        }
        if (mimetype === 'message/rfc822') {
            logger.info('   overwriting Headers (message/rfc822)');
            res.set('content-type', 'message/rfc822');
            // eslint-disable-next-line max-len, prefer-template
            res.set('content-disposition', 'attachment; filename="someName.eml"');
            logger.info('Document Download Headers (overridden):', res.headers);
        }
        res.send(documentBody) /* Sending message to clinet */
    }
})
.on('data', (d) => {
  console.log('We are debugging here')
})

Рабочее событие на основе / передано версия:

const r = request
    .get(url)
    .on('response', (documentsResponse) => {
        if (Number.parseInt(documentsResponse.headers['content-length'], 10) !== 0) {
            // Überschreibe headers für PDF und TIFF, diese kommen gelegentlich unvollständig an
            if (mimetype === 'application/pdf') {
                logger.info('   overwriting Headers (PDF)');
                res.set('content-type', 'application/pdf');
                res.set('content-disposition', 'inline; filename="someName".pdf"')
                logger.info('Document Download Headers (overridden):', documentsResponse.headers);
            }
            if (mimetype === 'message/rfc822') {
                logger.info('   overwriting Headers (message/rfc822)');
                res.set('content-type', 'message/rfc822');
                res.set('content-disposition', 'attachment; filename="someName".eml"');
                logger.info('Document Download Headers (overridden):', res.headers);
            }
            r.pipe(res); /* Response is piped to client */
        } else {
            res.redirect(get404Navigation());
        }
    }
   .on('data', (d) => {
     console.log('We are debugging here')
   })

Событие, в котором часть с r.pipe(res) кажется очень подозрительным (см., где объявлено r и где используется), это версии, которые работают правильно для обоих случаев.

Я предполагаю, что эта проблема может быть вызвана природой отправки многокомпонентного контента, поэтому я добавил дополнительные on('data', (d)=>{}) обратные вызовы и установил точки прерывания, чтобы видеть, когда завершается / передается ответ, когда вызывается обработчик данных, и результаты соответствуют моим ожиданиям :

request(url, (err, response, body)) случай, обработчик данных вызывается дважды, перед выполнением обратного вызова, все тело доступно внутри обработчика, поэтому мне еще более неясно, что я не могу просто res.send его. request.get(url).on('response') Сначала вызывается piping to res, затем вызывается обработчик данных два раза. Я полагаю, что внутренние внутренности механизма HTTP node.js выполняют асинхронный трюк и выдвигают ответы один за другим при получении каждого чанка ответа.

Я буду рад любому объяснению, что я делаю неправильно и что я могу выровнять, чтобы заставить мою версию обратного вызова работать как положено для случая PDF.

Эпилог: Почему такой код используется? Наш бэкэнд извлекает данные PDF с внешнего, не подверженного воздействию publi c inte rnet сервера, но из-за устаревших причин некоторые заголовки установлены неправильно (в основном Content-Disposition), поэтому мы перехватываем их и действуем как своего рода прокси выравнивания между источником данных и клиентом.

...