You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Node.js 16第二代GCF生成二维码时间歇性出现响应截断问题

第二代Google Cloud Function生成二维码出现截断响应问题

我有一个基于onRequest的第二代Google Cloud Function(GCF),运行在Node.js 16环境,使用node-qrcode包生成简单二维码。

二维码示例

第二代GCF具备高并发处理能力,但多数请求返回正常二维码的同时,约5%的请求返回黑方块,且GCF日志中出现如下提示:

Truncated response body. Usually implies that the request timed out or the application exited before the response was finished.

我已配置1个常驻实例、2GiB内存、60秒超时,生成该类简单二维码应无资源或超时问题。我怀疑存在Promise或await使用不当的问题,同时不清楚该截断提示的具体成因,GCF追踪未提供更多细节。注:二维码编码数据始终为整数格式。

GCF的qrCode.ts脚本

import { onRequest } from 'firebase-functions/v2/https'
import { logger } from 'firebase-functions/v2'

import QRCode from 'qrcode'
import express from 'express'
import cors from 'cors'
import { kDebug } from './lib/constants'
import { normalizedSku } from './lib/helperFunctions'

const app = express()

// 自动允许跨域请求
app.use(cors({ origin: true }))
// 支持JSON编码和URL编码的请求体
app.use(express.urlencoded({ extended: true }))

// 将二维码保存到GCF的临时内存并以文件形式返回
const filepath = '/tmp/qrcode.png'

/**
 * 将二维码保存到文件。
 * @param {string} qrCodePlainText 构成二维码的明文内容。
 * @param {unknown} options 二维码生成包的配置选项。
 * @return {boolean} 二维码是否成功保存到文件。
 */
async function saveQrCodeToFile(qrCodePlainText: string, options: unknown): Promise<boolean> {
  try {
    await QRCode.toFile(filepath, qrCodePlainText, options)
    return true
  } catch (error) {
    logger.error(error)
    return false
  }
}

/**
 * 二维码图片生成器。GET请求中的值会被解码并作为生成二维码的明文内容,最终以PNG格式返回。
 */
app.get('/qrCode/:data', async (req, res) => {
  let qrCodeData = req.params.data
  // 尺寸单位为英寸,例如值为1时生成1"x1"的二维码,最大值为10
  const inchesParam = req.query.inches
  const returnToDataUrl = req.query.returnToDataUrl === 'true'
  const isSku = req.query.isSku === 'true'

  try {
    if (kDebug) {
      logger.info('请求参数', req.params)
      logger.info('查询参数', req.query)
    }

    if (!qrCodeData) {
      throw Error('缺少二维码数据参数')
    }

    if (!inchesParam) {
      throw Error('缺少尺寸(英寸)参数')
    }

    // 如果不是数字则返回NaN
    const inches = parseFloat(inchesParam as string)

    if (isNaN(inches)) {
      throw Error(`${inchesParam} 不是有效数字`)
    }

    if (kDebug) {
      logger.info(`二维码尺寸:${inches}"x${inches}"`)
    }

    if (inches > 10) {
      throw Error('尺寸参数不能大于10')
    }

    // 如果是SKU则标准化数据
    if (isSku) {
      qrCodeData = normalizedSku(qrCodeData)
    }

    const options = {
      margin: 2,
    }

    // 将英寸转换为像素(1英寸=96像素)
    options['width'] = Math.round(inches * 96)

    if (kDebug) {
      logger.info(options)
    }

    const qrCodePlainText = decodeURIComponent(qrCodeData)

    if (returnToDataUrl) {
      res.send(await QRCode.toDataURL(qrCodePlainText))
      return
    } else {
      if (!(await saveQrCodeToFile(qrCodePlainText, options))) {
        throw Error(`二维码生成失败,数据为:${qrCodeData}`)
      }
      res.sendFile(filepath)
    }
  } catch (e) {
    if (kDebug) {
      logger.error(e)
    }

    res.status(400).send(e.toString())
  }
})

exports.endpoint = onRequest(
  { timeoutSeconds: 60, region: ['us-east4'], memory: '2GiB', minInstances: 1 },
  app,
)

修改后的saveQrCodeToFile()函数(使用回调)

/**
 * 将二维码保存到文件。
 * @param {string} qrCodePlainText 构成二维码的明文内容。
 * @param {unknown} options 二维码生成包的配置选项。
 * @return {boolean} 二维码是否成功保存到文件。
 */
async function saveQrCodeToFile(qrCodePlainText: string, options: unknown): Promise<boolean> {
  try {
    return new Promise((resolve, reject) => {
      QRCode.toFile(filepath, qrCodePlainText, options, error => {
        if (error) {
          reject(error)
        }

        resolve(true)
      })
    })
  } catch (error) {
    logger.error(error)
    return false
  }
}

内容的提问来源于stack exchange,提问作者Zelf

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 04:50:19