Lambda PDF代理函数运行超时、无法返回Base64编码PDF问题求助
Lambda函数抓取PDF问题排查
目标
传入HTTP请求参数抓取PDF文件,并通过Lambda服务将文件转发给请求方。
当前代码
"use strict"; const http = require("http"); function getPDF(options, event) { console.log(options); return new Promise((resolve, reject) => { let body = ""; let statusCode = 0; let headers = { }; http .request(options, (res) => { statusCode = res.statusCode; const headersFromReq = res.headers || {}; res.on("data", (chunk) => (body += chunk)); res.on("end", function () { console.log( statusCode, headers, body); resolve({ body: Buffer.from(body).toString(), statusCode, headers: { ...headersFromReq, //'Content-type': 'application/pdf', //'content-disposition': 'attachment; filename=test.pdf' } }); }) .on("error", reject) .end(); }); }); } exports.handler = async (event) => { try { const response = await getPDF(event.options, event); return response; } catch (error) { console.error(error); return { statusCode: 500, body: JSON.stringify(error), headers: {} }; } };
问题现象
无论如何调试,该函数要么触发超时,要么无法返回预期的Base64编码PDF结果。
测试参数示例
{ "options": { "hostname": "www.africau.edu", "port": 80, "path": "images/default/sample.pdf", "method": "GET", "headers": { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.122 Safari/537.36", "Accept": "application/pdf", "Accept-encoding": "gzip, deflate, br" } } }
当前运行日志
Function Logs START RequestId: 8d6be86c-788d-4f49-8305-8caf377cd32e Version: $LATEST 2021-09-28T09:01:21.507Z 8d6be86c-788d-4f49-8305-8caf377cd32e INFO { hostname: 'www.africau.edu', port: 80, path: 'images/default/sample.pdf', method: 'GET', headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.122 Safari/537.36', Accept: 'application/pdf', 'Accept-encoding': 'gzip, deflate, br' } } END RequestId: 8d6be86c-788d-4f49-8305-8caf377cd32e REPORT RequestId: 8d6be86c-788d-4f49-8305-8caf377cd32e Duration: 11011.54 ms Billed Duration: 11000 ms Memory Size: 128 MB Max Memory Used: 54 MB Init Duration: 152.43 ms 2021-09-28T09:01:32.494Z 8d6be86c-788d-4f49-8305-8caf377cd32e Task timed out after 11.01 seconds
问题排查结果
1. 超时原因
- 错误的事件绑定位置:当前代码将
error事件绑定到了响应对象res上,而非http.request返回的请求对象,请求阶段出现的网络错误、DNS解析失败等问题无法被捕获,会导致Promise一直处于pending状态,直到Lambda超时。 - 请求路径错误:测试参数中的
path字段缺少前导斜杠,正确写法应为/images/default/sample.pdf,路径格式错误会导致目标服务器无法正确响应请求。 - 压缩编码未处理:请求头中声明接受
gzip、deflate、br压缩格式,但原生http模块不会自动处理压缩响应,会导致内容接收异常。
2. 无法返回正确PDF的原因
- 二进制内容处理错误:PDF是二进制文件,代码中用字符串
body += chunk拼接二进制数据会直接损坏文件内容。 - 缺少Base64编码配置:Lambda返回二进制文件需要将body转成Base64编码,同时添加
isBase64Encoded: true的配置字段,否则前端会收到乱码内容。
修复后代码
"use strict"; const http = require("http"); const zlib = require("zlib"); function getPDF(options, event) { console.log(options); return new Promise((resolve, reject) => { // 统一用Buffer存储二进制内容,避免字符串拼接损坏数据 const chunks = []; let statusCode = 0; const req = http.request(options, (res) => { statusCode = res.statusCode; const headersFromReq = res.headers || {}; // 自动处理压缩响应 let stream = res; const contentEncoding = headersFromReq['content-encoding']; if (contentEncoding === 'gzip') { stream = res.pipe(zlib.createGunzip()); } else if (contentEncoding === 'deflate') { stream = res.pipe(zlib.createInflate()); } else if (contentEncoding === 'br') { stream = res.pipe(zlib.createBrotliDecompress()); } stream.on("data", (chunk) => chunks.push(chunk)); stream.on("end", function () { const buffer = Buffer.concat(chunks); resolve({ // 转Base64编码,标记为Base64格式 isBase64Encoded: true, body: buffer.toString('base64'), statusCode, headers: { ...headersFromReq, 'Content-type': 'application/pdf', 'content-disposition': 'attachment; filename=test.pdf' } }); }); stream.on("error", reject); }); // 请求对象绑定错误事件,捕获请求阶段异常 req.on("error", reject); req.end(); }); } exports.handler = async (event) => { try { // 自动补全路径前导斜杠,避免参数格式错误 if (!event.options.path.startsWith('/')) { event.options.path = '/' + event.options.path; } const response = await getPDF(event.options, event); return response; } catch (error) { console.error(error); return { statusCode: 500, body: JSON.stringify({msg: '请求失败', error: error.message}), headers: {} }; } };
测试参数修改建议
将options.path修改为/images/default/sample.pdf即可正常请求测试用的PDF文件。
内容的提问来源于stack exchange,提问作者M K
相关产品推荐
相关产品推荐

