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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.08 19:07:59