Express调用GCS流式下载时无法捕获文件不存在错误如何解决
问题根源
createReadStream()方法返回的是流对象,流运行过程中发生的错误(比如文件不存在、网络异常)是通过error事件触发的,不属于Promise异常,也不会被try/catch捕获pipe()方法返回的是目标流,本身不是Promise,await在这里没有任何实际作用,不会等待流传输完成,也不会捕获流的错误- 你遇到的报错是典型的流错误未被监听,Node.js遇到未处理的
error事件会直接崩溃进程
修复方案
你可以通过监听流的error事件处理异常,也可以先判断文件是否存在再返回流,推荐修复代码如下:
const express = require('express') const {Storage} = require('@google-cloud/storage'); let server = express() const storage = new Storage(); const bucketName = '<some-bucket-name>' server.get('/*', async (req, res) => { const widthString = req.query.width const heightString = req.query.height const format = req.query.format const fileName = req.path.substring(1); console.log("url: ", req.path) let width, height if (widthString) { width = parseInt(widthString) } if (heightString) { height = parseInt(heightString) } const file = storage.bucket(bucketName).file(fileName) // 先判断文件是否存在,提前拦截404 const [exists] = await file.exists().catch(err => { console.error('检查文件存在性失败:', err) return [false] }) if (!exists) { res.status(404).send('图片不存在') return } res.type(`image/${format || 'png'}`) const readStream = file.createReadStream() // 监听流错误,避免进程崩溃 readStream.on('error', err => { console.error('读取文件流失败:', err) if (!res.headersSent) { res.status(500).send('读取资源失败') } }) readStream.pipe(res) })
额外优化建议
也可以直接使用Node.js官方提供的pipeline方法替代pipe,pipeline会自动处理流的错误、资源销毁,并且支持Promise封装,更适合异步场景:
// 顶部引入pipeline const { pipeline } = require('stream/promises') // 流传输部分替换为如下代码,原有try/catch可以正常捕获错误 try { res.type(`image/${format || 'png'}`) await pipeline(file.createReadStream(), res) } catch (err) { console.error('流传输失败:', err) if (!res.headersSent) { res.status(err.code === 404 ? 404 : 500).send('资源访问失败') } }
内容的提问来源于stack exchange,提问作者Aditya Mertia
相关产品推荐
相关产品推荐

