Lambda上传文件至AWS S3失败及参数错误问题求助
解决Lambda抓取网页生成CSV上传S3的两个核心问题
先帮你拆解一下遇到的两个问题,一步步来解决:
问题1:移除async后出现「Bad argument」错误
这个错误的根源是**request的回调参数顺序写错了**!request的标准回调签名是 function(error, response, body),但你写的是 function(response, body)——这就导致你拿到的body其实是response对象,真正的响应体根本没传进Iconv.convert(),自然会抛出参数错误。
先把这行代码修正:
// 错误写法 request({ url: 'https://finance.naver.com/sise/sise_quant.nhn', encoding: null }, function(response, body) { ... }) // 正确写法 request({ url: 'https://finance.naver.com/sise/sise_quant.nhn', encoding: null }, function(error, response, body) { // 先加个错误判断,避免后续崩溃 if (error) { console.error('请求网页失败:', error); return; } // 剩下的逻辑不变... })
问题2:Async模式下Lambda显示Success但S3无文件
当你用async修饰handler时,Lambda会等待函数返回的Promise完成才结束。但你用的是request的回调风格,没有把异步请求包装成Promise,Lambda会在request还没发起请求/处理完响应的时候就提前结束了,导致S3上传代码根本没执行。
解决这个问题有两种方式:
方式1:把request改成Promise风格(推荐)
可以用request-promise替代原生request(需要把它加到你的Lambda层里),这样就能用async/await等待请求完成:
const rp = require('request-promise'); // 然后在handler里: exports.handler = async (event) => { class Stock { /* ... 你的类定义不变 ... */ } try { // 用rp发送请求,await等待结果 const body = await rp({ url: 'https://finance.naver.com/sise/sise_quant.nhn', encoding: null }); const euckr_to_utf8 = new Iconv('euc-kr', 'utf8'); const koreanHTML = euckr_to_utf8.convert(body).toString(); const $ = cheerio.load(koreanHTML); // 先创建一个数组收集所有股票数据,避免循环里多次上传覆盖文件 const stocks = []; const table = $('.type_2').children('tbody').children('tr'); for(let i = 1; i < table.length; ++i) { const tr = table[i]; if(tr.children.length === 1) continue; const tds = tr.children; // 初始化变量 let name, rank, price, change_inValue, change_inPercent, updown; for(let j = 0; j < tds.length; ++j) { const td = tds[j]; if(td.type == 'text') continue; const td_data = td.children; // 你的解析逻辑不变,最后把stock推入数组 if(j == 9) { // ... 你的updown判断逻辑 ... const stock = new Stock( name, rank, price, change_inValue, change_inPercent, updown); stocks.push(stock); } } } // 生成CSV内容 const csv = new ObjectsToCsv(stocks); const csvContent = await csv.toString(); // 一次性上传到S3(注意Key要加.csv后缀,不然可能识别不了) await s3.putObject({ Bucket: BUCKET_NAME, Key: `${date_string}.csv`, // 比如改成 "10/25.csv" ContentType: 'text/csv', Body: csvContent }).promise(); // 记得加.promise()让AWS SDK返回Promise return { statusCode: 200, body: `成功上传${stocks.length}条数据到S3` }; } catch (error) { console.error('处理失败:', error); return { statusCode: 500, body: error.message }; } };
方式2:用回调包装Promise(不用换库)
如果你不想换request-promise,可以把原生request包装成Promise:
exports.handler = async (event) => { class Stock { /* ... 不变 ... */ } // 包装request为Promise const fetchPage = () => { return new Promise((resolve, reject) => { request({ url: 'https://finance.naver.com/sise/sise_quant.nhn', encoding: null }, function(error, response, body) { if (error) reject(error); else resolve(body); }); }); }; try { const body = await fetchPage(); // 后面的解析、收集数据、生成CSV、上传S3逻辑和方式1一样 } catch (error) { // 错误处理 console.error('处理失败:', error); return { statusCode: 500, body: error.message }; } };
额外优化点
- 你原来的代码在循环里每次创建Stock就调用
putObject,这会导致同一个Key被多次覆盖,最后S3里只会留下最后一条数据。所以一定要先把所有Stock收集到数组里,再一次性生成CSV上传。 - S3的Key建议加上
.csv后缀,方便后续识别和打开。 - 记得给Lambda层加上
request-promise(如果用方式1),或者确保原生request已经在层里。
内容的提问来源于stack exchange,提问作者yujonglee
相关产品推荐
相关产品推荐

