如何在Node.js+Fastify中正确返回AWS SdkStream?
解决方案
问题出在AWS SDK v3返回的SdkStream并非标准Node.js可读流,Fastify直接处理时会引发头信息与流传输的时序冲突,导致报错。解决步骤如下:
- 安装依赖(如果未安装):
npm install @aws-sdk/util-stream-node
转换SdkStream为标准Node流:
导入sdkStreamMixin工具,将S3返回的Body转换为Fastify可正确处理的Node.js可读流。编码文件名:
用encodeURIComponent处理文件名,避免特殊字符破坏响应头格式。
修改后的核心代码片段:
import { sdkStreamMixin } from "@aws-sdk/util-stream-node"; // ... 其余代码保持不变 const { Body } = await s3Client.send(command); // 转换为标准Node.js可读流 const nodeReadableStream = sdkStreamMixin(Body); return reply .header(`Content-disposition`, `attachment; filename="${encodeURIComponent(filename)}"`) .type(mimetype) .send(nodeReadableStream);
完整的API处理函数:
server.get( `${basePath}/download/:file_uuid`, { preHandler: (request, reply, done) => BasicAuthMiddleware(server, request, reply, done) }, async (request, reply) => { try { const fileUUID = request.params.file_uuid; const bucket = process.env.aws_bucket_name; const accessKeyId = process.env.accessKeyId; const secretAccessKey = process.env.secretAccessKey; if(!accessKeyId) throw new MissingParameter('AWS accessKeyId is missing', 500) if(!secretAccessKey) throw new MissingParameter("AWS secretAccessKey is missing", 500); if(!bucket) throw new MissingParameter("Bucket name is missing from configs", 500) if(!fileUUID) { throw new MissingParameter("File UUID is missing", 500) } const s3Configs: S3ClientConfig = { region: process.env.aws_region, credentials: { accessKeyId: process.env.accessKeyId!, secretAccessKey: process.env.secretAccessKey! } } // 获取文件元信息 const storedFileInfos = await server.pool_db.query(` SELECT * FROM ${schema}.files WHERE s3_filename = $1`, [ fileUUID ]); if(!storedFileInfos?.rows?.length) throw new Error("File not found in saved uploads") const storedFile = storedFileInfos.rows[0]; const { filename, mimetype } = storedFile; // 从S3获取文件并转换流 const s3Client = new S3Client(s3Configs) as NodeJsClient<S3Client>; const command = new GetObjectCommand({ Bucket: bucket, Key: fileUUID }) const { Body } = await s3Client.send(command); const nodeReadableStream = sdkStreamMixin(Body); return reply .header(`Content-disposition`, `attachment; filename="${encodeURIComponent(filename)}"`) .type(mimetype) .send(nodeReadableStream); } catch(error: any) { console.error(error); return reply.code(error.status ?? 500).send({ statusCode: error.status ?? 500, message: error.message }) } } )
错误原因说明:
Fastify在处理流响应时,依赖标准Node.js可读流的事件机制来同步头信息写入与流传输。AWS SDK v3的SdkStream是自定义的异步迭代器实现,未兼容Node流的完整接口,导致Fastify无法正确处理流的生命周期,触发"stream closed prematurely"和头信息写入冲突的错误。转换为标准Node流后,Fastify就能按预期处理响应流程。
内容的提问来源于stack exchange,提问作者Geeogee
相关产品推荐
相关产品推荐

