amazon s3.upload耗时导致成功上传文件名列表返回空如何解决
问题核心原因
S3.upload()是异步IO操作,你当前使用的同步遍历逻辑不会等待上传任务执行完成就会继续执行后续代码,因此用来存储结果的数组还没来得及写入上传成功的文件名就被返回,最终得到空响应。
同时原代码还存在两处逻辑错误:一是s3FileName变量的字符串引号未闭合,二是错误判断原文件对象是否存在ETag字段,该字段是S3上传成功后才会返回的属性,应该校验上传接口的返回结果。
修复方案
需要将上传逻辑改为异步等待模式:
- 给当前逻辑所在的外层函数添加
async关键字,支持异步等待语法 - 替换
forEach遍历为map,将每个文件的上传任务封装为Promise对象 - 通过
Promise.all等待所有上传任务执行完成后,再统一过滤收集成功上传的文件名
修复后代码
// 注意:当前逻辑所在的外层函数必须添加async关键字,示例:async function yourUploadHandler() { if (formObject.files.document && formObject.files.document.length > 0) { const circleCode = formObject.fields.circleCode[0]; // 把每个文件的上传任务封装为Promise const uploadPromises = formObject.files.document.map(async e => { const extractFileExtension = ".pdf"; if (_.has(FILE_EXTENSIONS_INCLUDED, _.lowerCase(extractFileExtension))) { console.log(e); // 文件名清理逻辑 const originalFileNameCleaned = "cleaning name logic"; const _id = mongoose.Types.ObjectId(); const s3FileName = "s3-filename-convension"; // 修复原代码漏写的字符串闭合引号 console.log(e.path, "", s3FileName); try { // 等待S3上传完成 const awsResponse = await new File().uploadFileOnS3(e.path, s3FileName); // 校验上传成功后返回重命名的文件名 if(awsResponse && awsResponse.ETag) { return awsResponse.key.split("/")[1]; } } catch (err) { // 单个文件上传失败的异常处理,可自行打日志上报 console.error('文件上传失败:', err); } } // 不符合格式要求或上传失败返回空值,后续统一过滤 return null; }); // 等待所有上传任务执行完成,过滤掉空值得到最终成功的文件名列表 const collectedKeysFromAwsResponse = (await Promise.all(uploadPromises)).filter(Boolean); // 此处可直接返回collectedKeysFromAwsResponse作为接口响应 console.log('成功上传的文件列表:', collectedKeysFromAwsResponse); };
可选优化
- 如果上传文件数量较大,且允许部分文件上传失败,可将
Promise.all替换为Promise.allSettled,可以获取每个上传任务的成功/失败状态,便于后续对失败任务做重试处理 - 可以根据业务需要调整异常处理逻辑,比如单个文件上传失败时直接抛出异常终止整个流程,或者收集失败的文件名返回给前端提示
内容的提问来源于stack exchange,提问作者Nick
相关产品推荐
相关产品推荐

