AWS Lambda执行完毕后,/tmp目录下的临时文件是否会自动删除?
我有一个Node.js函数,用户可通过它上传视频文件到S3存储桶。我配置了Lambda函数监听这个存储桶的上传事件,每有视频上传就会用ffmpeg转码,之后把转码后的文件存入另一个存储桶,同时删除原文件。
在Lambda里用ffmpeg的过程折腾了好久,现在功能已经跑通,但有个小问题:用fluent-ffmpeg的时候没法直接通过流的方式读写S3存储桶。
我的核心代码如下:
new Promise((resolve, reject) => { ffmpeg().input(`https://s3.eu-central-1.amazonaws.com/mybucket.mywebsite.com/${key}`) .addInputOption('-analyzeduration 100M') .addInputOption('-probesize 100M') .toFormat('mp4') .addOutputOption("-preset veryfast") .addOutputOption("-movflags frag_keyframe+empty_moov") .addOutputOption("-crf 28") .output('/tmp/output.mp4') .on('error', (err, stdout, stderr) => { console.log(err); console.log('Stdout: %o', stdout); console.log('Stderr: %o', stderr); reject(err); }) .on('end', () => { resolve(); }) .run(); })
如你所见,我直接读取视频文件的URL作为输入。之前试过创建读取流(处理图片时这么做没问题),但没成功。
转码完成后,我会执行下面的代码把文件传到目标存储桶:
await putObjectToS3(process.env.MY_NEW_BUCKET, newKey, '/tmp/output.mp4', mimeType);
这个流程能正常跑,但我有个疑问:Lambda函数执行完之后,/tmp/output.mp4会怎么样?是自动删除,还是一直留着要我手动删?我不需要这个文件了,不想为它的存储付费。
Lambda的/tmp目录是临时存储区,它的生命周期和Lambda的执行环境绑定:
- 当函数执行完成(不管是成功结束还是报错终止),如果这个执行环境被Lambda回收,
/tmp里的文件会自动被删掉。 - 但要注意:Lambda可能会复用之前的执行环境(也就是热启动场景),这种情况下
/tmp里的旧文件会保留下来。不过这种复用是不可预测的,而且你不用担心付费问题——Lambda的临时存储是包含在函数执行费用里的,不会额外收存储费。
不过从最佳实践来说,建议你在上传完S3之后手动删除/tmp里的文件,比如调用fs.unlinkSync('/tmp/output.mp4'),这样既能避免环境复用时占用不必要的空间,也能彻底打消你对文件留存的顾虑。
另外关于你提到的fluent-ffmpeg没法直接用流读写S3的问题,给你个小技巧:可以用@aws-sdk/client-s3获取S3对象的可读流,传给fluent-ffmpeg的input方法,同时把转码后的输出流直接上传到S3,完全不用写/tmp文件。示例代码大概是这样:
const { GetObjectCommand, PutObjectCommand, S3Client } = require("@aws-sdk/client-s3"); const { PassThrough } = require('stream'); const s3Client = new S3Client({ region: 'eu-central-1' }); // 获取原S3文件的可读流 const getObjectCmd = new GetObjectCommand({ Bucket: '原存储桶名称', Key: key }); const s3Response = await s3Client.send(getObjectCmd); // 创建PassThrough流作为ffmpeg的输出目标 const outputStream = new PassThrough(); // 启动S3上传任务 const uploadPromise = s3Client.send(new PutObjectCommand({ Bucket: process.env.MY_NEW_BUCKET, Key: newKey, Body: outputStream, ContentType: mimeType })); // 配置ffmpeg从S3流读取,输出到PassThrough流 ffmpeg(s3Response.Body) .addInputOption('-analyzeduration 100M') .addInputOption('-probesize 100M') .toFormat('mp4') .addOutputOption("-preset veryfast") .addOutputOption("-movflags frag_keyframe+empty_moov") .addOutputOption("-crf 28") .pipe(outputStream) .on('error', (err) => { throw err; }) .on('end', async () => { await uploadPromise; // 这里可以添加删除原S3文件的逻辑 });
这种方式跳过了临时文件,效率更高,也减少了磁盘操作可能带来的问题。
内容的提问来源于stack exchange,提问作者Sam Leurs

