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

AWS Lambda中ffmpeg-static需何权限?错误码126问题排查

问题描述

我在AWS Lambda上部署了一段代码,功能为下载图片、生成视频并上传至S3。执行以下命令打包上传:

npm install --production
zip -r my-lambda-function.zip ./

但出现错误码126:

2023-02-17T09:27:55.236Z    5c845bb6-02c1-41b0-8759-4459591b57b0    INFO    Error: ffmpeg exited with code 126
    at ChildProcess.<anonymous> (/var/task/node_modules/fluent-ffmpeg/lib/processor.js:182:22)
    at ChildProcess.emit (node:events:513:28)
    at ChildProcess._handle.onexit (node:internal/child_process:291:12)
2023-02-17T09:27:55.236Z 5c845bb6-02c1-41b0-8759-4459591b57b0 INFO Error: ffmpeg exited with code 126 at ChildProcess.<anonymous> (/var/task/node_modules/fluent-ffmpeg/lib/processor.js:182:22) at ChildProcess.emit (node:events:513:28) at ChildProcess._handle.onexit (node:internal/child_process:291:12)

请问是否需要为ffmpeg设置特定权限?我遗漏了什么?

代码实现

import { PutObjectCommand, S3Client } from '@aws-sdk/client-s3'
import { fromNodeProviderChain } from '@aws-sdk/credential-providers'
import axios from 'axios'
import pathToFfmpeg from 'ffmpeg-static'
import ffmpeg from 'fluent-ffmpeg'
import fs from 'fs'
ffmpeg.setFfmpegPath(pathToFfmpeg)
const credentials = fromNodeProviderChain({
    clientConfig: {
        region: 'eu-central-1',
    },
})
const client = new S3Client({ credentials })

export const handler = async (event, context) => {
    try {
        let body
        let statusCode = 200
        const query = event?.queryStringParameters
        if (!query?.imgId && !query?.video1Id && !query?.video2Id) {
            return
        }

        const imgId = query?.imgId
        const video1Id = query?.video1Id
        const video2Id = query?.video2Id
        console.log(
            `Parameters received, imgId: ${imgId}, video1Id: ${video1Id}, video2Id: ${video2Id}`
        )
        const imgURL = getFileURL(imgId)
        const video1URL = getFileURL(`${video1Id}.mp4`)
        const video2URL = getFileURL(`${video2Id}.mp4`)
        const imagePath = `/tmp/${imgId}`
        const video1Path = `/tmp/${video1Id}.mp4`
        const video2Path = `/tmp/${video2Id}.mp4`
        const outputPath = `/tmp/${imgId}.mp4`
        await Promise.all([
            downloadFile(imgURL, imagePath),
            downloadFile(video1URL, video1Path),
            downloadFile(video2URL, video2Path),
        ])
        await new Promise((resolve, reject) => {
            console.log('Input files downloaded')
            ffmpeg()
                .input(imagePath)
                .inputFormat('image2')
                .inputFPS(30)
                .loop(1)
                .size('1080x1080')
                .videoCodec('libx264')
                .format('mp4')
                .outputOptions([
                    '-tune animation',
                    '-pix_fmt yuv420p',
                    '-profile:v baseline',
                    '-level 3.0',
                    '-preset medium',
                    '-crf 23',
                    '-movflags +faststart',
                    '-y',
                ])
                .output(outputPath)
                .on('end', () => {
                    console.log('Output file generated')
                    resolve()
                })
                .on('error', (e) => {
                    console.log(e)
                    reject()
                })
                .run()
            
        })
        await uploadFile(outputPath, imgId + '.mp4')
            .then((url) => {
                body = JSON.stringify({
                    url,
                })
            })
            .catch((error) => {
                console.error(error)
                statusCode = 400
                body = error?.message ?? error
            })
        console.log(`File uploaded to S3`)
        const headers = {
            'Content-Type': 'application/json',
            'Access-Control-Allow-Headers': 'Content-Type',
            'Access-Control-Allow-Origin': 'https://tikex.com, https://borespiac.hu',
            'Access-Control-Allow-Methods': 'GET',
        }
        return {
            statusCode,
            body,
            headers,
        }
    } catch (error) {
        console.error(error)
        return {
            statusCode: 500,
            body: JSON.stringify('Error fetching data'),
        }
    }
}

const downloadFile = async (url, path) => {
    try {
        console.log(`Download will start: ${url}`)
        const response = await axios(url, {
            responseType: 'stream',
        })
        if (response.status !== 200) {
            throw new Error(
                `Failed to download file, status code: ${response.status}`
            )
        }
        response.data
            .pipe(fs.createWriteStream(path))
            .on('finish', () => console.log(`File downloaded to ${path}`))
            .on('error', (e) => {
                throw new Error(`Failed to save file: ${e}`)
            })
    } catch (e) {
        console.error(`Error downloading file: ${e}`)
    }
}
const uploadFile = async (path, id) => {
    const buffer = fs.readFileSync(path)
    const params = {
        Bucket: 't44-post-cover',
        ACL: 'public-read',
        Key: id,
        ContentType: 'video/mp4',
        Body: buffer,
    }
    await client.send(new PutObjectCommand(params))
    return getFileURL(id)
}
const getFileURL = (id) => {
    const bucket = 't44-post-cover'
    const url = `https://${bucket}.s3.eu-central-1.amazonaws.com/${id}`
    return url
}

Lambda权限策略

已添加AWSLambdaBasicExecutionRole-16e770c8-05fa-4c42-9819-12c468cb5b49权限,策略如下:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "logs:CreateLogGroup",
            "Resource": "arn:aws:logs:eu-central-1:634617701827:*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            "Resource": [
                "arn:aws:logs:eu-central-1:634617701827:log-group:/aws/lambda/promo-video-composer-2:*"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "s3:GetObject",
                "s3:PutObject",
                "s3:ListBucket"
            ],
            "Resource": [
                "arn:aws:s3:::example-bucket",
                "arn:aws:s3:::example-bucket/*"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogGroup",
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            "Resource": [
                "arn:aws:logs:*:*:*"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "ec2:DescribeNetworkInterfaces"
            ],
            "Resource": [
                "*"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "sns:*"
            ],
            "Resource": [
                "*"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "cloudwatch:*"
            ],
            "Resource": [
                "*"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "kms:Decrypt"
            ],
            "Resource": [
                "*"
            ]
        }
    ]
}

本地ffmpeg信息

janoskukoda@Janoss-MacBook-Pro promo-video-composer-2 % ls -l $(which ffmpeg)
lrwxr-xr-x  1 janoskukoda  admin  35 Feb 10 12:50 /opt/homebrew/bin/ffmpeg -> ../Cellar/ffmpeg/5.1.2_4/bin/ffmpeg

解决方案

错误码126表示无法执行ffmpeg二进制文件,核心原因是Lambda运行环境(Amazon Linux 2)中,ffmpeg-static安装的二进制文件没有可执行权限,或者本地打包时的二进制文件与Lambda架构不兼容。

具体修复步骤:

  1. 修复ffmpeg二进制文件权限
    打包前,确保node_modules/ffmpeg-static目录下的二进制文件拥有可执行权限。在本地执行:

    chmod +x node_modules/ffmpeg-static/ffmpeg
    

    然后再重新打包:

    zip -r my-lambda-function.zip ./
    
  2. 确认架构兼容性
    Lambda默认使用x86_64架构,如果你在Apple Silicon(arm64)Mac上安装ffmpeg-static,会下载arm架构的二进制文件,放到x86_64的Lambda环境中无法运行。解决方法:

    • 强制安装x86_64版本的ffmpeg-static:
      npm install ffmpeg-static --arch=x64 --platform=linux
      
    • 或者在Lambda控制台中将函数架构切换为arm64(对应Graviton2处理器)。
  3. 修正S3权限匹配问题
    你的Lambda策略中S3权限指向的是example-bucket,但代码中实际使用的是t44-post-cover桶,需要修改策略中的Resource字段,替换为实际桶的ARN:

    {
        "Effect": "Allow",
        "Action": [
            "s3:GetObject",
            "s3:PutObject",
            "s3:ListBucket"
        ],
        "Resource": [
            "arn:aws:s3:::t44-post-cover",
            "arn:aws:s3:::t44-post-cover/*"
        ]
    }
    
  4. 修复下载函数的异步逻辑
    当前downloadFile函数使用pipe但没有正确返回Promise,可能导致文件未完全下载就执行ffmpeg命令,引发错误。修改downloadFile:

    const downloadFile = async (url, path) => {
        try {
            console.log(`Download will start: ${url}`)
            const response = await axios(url, {
                responseType: 'stream',
            })
            if (response.status !== 200) {
                throw new Error(`Failed to download file, status code: ${response.status}`)
            }
            return new Promise((resolve, reject) => {
                response.data
                    .pipe(fs.createWriteStream(path))
                    .on('finish', () => {
                        console.log(`File downloaded to ${path}`)
                        resolve()
                    })
                    .on('error', (e) => {
                        reject(new Error(`Failed to save file: ${e}`))
                    })
            })
        } catch (e) {
            console.error(`Error downloading file: ${e}`)
            throw e
        }
    }
    

额外建议

  • 可以考虑使用Lambda层来管理ffmpeg,避免每次打包都包含二进制文件,提升部署效率。
  • 测试时可以在Lambda中添加日志,打印pathToFfmpeg的值,确认路径正确,同时检查该路径下的文件权限。

内容的提问来源于stack exchange,提问作者János

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 04:12:21