使用@aws-sdk/client-s3 V3的GetObjectCommand下载S3文件随机损坏求助
排查Electron中从S3下载PDF随机损坏的问题
核心代码问题:未正确等待文件写入完成
你的代码里await objData.Body.pipe(...)逻辑无效——pipe()返回的是目标流而非Promise,await不会等待文件写入完成就进入下一次循环。这种情况下,若循环迭代速度快于文件写入速度,极易导致文件写入不完整,表现为PDF损坏。
修复方法是将流的finish事件包装为Promise,确保每一次文件写入完成后再处理下一个文件:
const downloadEachObject = async (files) => { let lastFile; const basePath = '/Users/downloadTestDir'; try { for (let i = 0; i < files.length; i++) { const file = files[i]; if (file.Key === lastFile) { continue; } lastFile = file.Key; const fileParams = { Bucket: 'bucket', Key: lastFile, }; const objData = await s3Client.send(new GetObjectCommand(fileParams)); const newFilePath = path.join(basePath, file.Key); // 先创建父目录(避免子目录不存在导致写入失败) const parentDir = path.dirname(newFilePath); await fs.promises.mkdir(parentDir, { recursive: true }); // 包装流事件为Promise,等待写入完成 await new Promise((resolve, reject) => { objData.Body.pipe(fs.createWriteStream(newFilePath)) .on('error', (error) => { console.error('写入文件出错: ', error); reject(error); }) .on('finish', () => { console.log('文件已写入磁盘: ', file.Key); resolve(); }); }); } } catch (er) { console.log('遍历文件出错', er); } };
其他排查方向
- 目录创建缺失:如果文件Key包含多级子目录(如
docs/report.pdf),直接调用createWriteStream会因父目录不存在而失败,导致文件写入不完整。上面的修复代码已通过mkdir({ recursive: true })处理该场景。 - 文件完整性校验:下载后对比本地文件哈希与S3对象的ETag(注意:分块上传的S3对象ETag会带
-<分块数>后缀,需去掉后缀再对比)。示例校验代码:const calculateMD5 = async (filePath) => { const hash = crypto.createHash('md5'); const stream = fs.createReadStream(filePath); await new Promise((resolve, reject) => { stream.on('data', (data) => hash.update(data)); stream.on('end', () => resolve()); stream.on('error', reject); }); return hash.digest('hex'); }; // 文件写入完成后执行校验 const localMD5 = await calculateMD5(newFilePath); const s3ETag = objData.ETag.replace(/"/g, '').split('-')[0]; if (localMD5 !== s3ETag) { console.error(`文件校验失败: ${file.Key},本地MD5: ${localMD5},S3 ETag: ${s3ETag}`); // 可在此添加重试逻辑 } - AWS SDK重试配置:网络波动可能导致下载中断,可调整S3客户端的重试策略:
const s3Client = new S3Client({ region: 'your-region', retryMode: 'standard', maxAttempts: 5, // 增加重试次数 }); - Electron文件权限:Electron环境下写入目录可能需要额外系统权限(如macOS的文件访问授权),可尝试将下载目录切换为Electron默认下载目录(
app.getPath('downloads'))测试。
内容的提问来源于stack exchange,提问作者Cris69
相关产品推荐
相关产品推荐

