如何通过S3预签名POST URL批量上传图片并实现预览?
多图上传与预览解决方案
一、调整Lambda函数支持多图预签名生成
当前Lambda仅生成单个预签名POST参数,要支持多图上传,需修改为接收前端传来的文件名列表,批量生成对应预签名数据:
import json import boto3 def lambda_handler(event, context): s3 = boto3.client('s3', region_name='us-east-1') bucket_name = 'testimagesbucketupload' # 从请求体中获取前端传入的文件名列表 filenames = json.loads(event['body']).get('filenames', []) if not filenames: return { 'statusCode': 400, 'body': json.dumps('未提供文件名列表') } presigned_posts = [] for filename in filenames: # 为每个文件名生成独立的预签名POST参数 post_data = s3.generate_presigned_post( Bucket=bucket_name, Key=filename, ExpiresIn=3600 ) presigned_posts.append({ 'filename': filename, 'url': post_data['url'], 'fields': post_data['fields'] }) return { 'statusCode': 200, 'body': json.dumps(presigned_posts) }
注意:确保API Gateway配置为接收POST请求并开启
Use Lambda Proxy integration,这样Lambda才能正确获取前端传入的请求体数据。
二、React端实现多图选择、预览与批量上传
1. 核心代码实现
import { useState } from 'react'; function ImageUploader() { const [selectedFiles, setSelectedFiles] = useState([]); const [uploadStatus, setUploadStatus] = useState({}); // 处理文件选择并生成本地预览 const handleFileChange = (e) => { const files = Array.from(e.target.files); const previewFiles = files.map(file => ({ file, previewUrl: URL.createObjectURL(file) })); setSelectedFiles(previewFiles); }; // 批量获取预签名并上传图片 const handleUpload = async () => { // 为文件名添加时间戳,避免S3文件覆盖 const filenames = selectedFiles.map(f => `${Date.now()}-${f.file.name}`); try { // 第一步:调用API Gateway获取所有图片的预签名参数 const presignedRes = await fetch('https://你的API网关地址/获取预签名路径', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ filenames }) }); const presignedPosts = await presignedRes.json(); // 第二步:逐个上传图片到S3 const uploadPromises = presignedPosts.map(async (post) => { const targetFile = selectedFiles.find(f => `${Date.now()}-${f.file.name}` === post.filename).file; const formData = new FormData(); // 追加预签名POST的所有固定字段 Object.entries(post.fields).forEach(([key, value]) => { formData.append(key, value); }); // 追加要上传的文件 formData.append('file', targetFile); const uploadRes = await fetch(post.url, { method: 'POST', body: formData }); // 更新对应文件的上传状态 setUploadStatus(prev => ({ ...prev, [post.filename]: uploadRes.ok ? '上传成功' : '上传失败' })); return uploadRes.ok; }); await Promise.all(uploadPromises); } catch (err) { console.error('上传出错:', err); } }; return ( <div> <input type="file" multiple onChange={handleFileChange} accept="image/*" /> <div style={{ display: 'flex', gap: '10px', marginTop: '10px' }}> {selectedFiles.map((f, idx) => ( <div key={idx} style={{ textAlign: 'center' }}> <img src={f.previewUrl} alt="预览图" style={{ width: '120px', height: '120px', objectFit: 'cover' }} /> <p style={{ margin: '5px 0' }}>{f.file.name}</p> <span>{uploadStatus[`${Date.now()}-${f.file.name}`] || ''}</span> </div> ))} </div> <button onClick={handleUpload} disabled={selectedFiles.length === 0} style={{ marginTop: '15px' }}> 批量上传 </button> </div> ); } export default ImageUploader;
2. 关键配置与注意事项
- S3 CORS配置:确保S3桶的CORS策略允许前端域名发起POST请求,示例规则:
<?xml version="1.0" encoding="UTF-8"?> <CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"> <CORSRule> <AllowedOrigin>你的前端域名</AllowedOrigin> <AllowedMethod>POST</AllowedMethod> <AllowedHeader>*</AllowedHeader> </CORSRule> </CORSConfiguration> - 文件名唯一性:通过添加时间戳或随机字符串避免S3中文件被覆盖,也可根据业务需求自定义命名规则。
- 错误处理:可扩展添加单个文件上传失败的重试逻辑,或更直观的错误提示组件。
内容的提问来源于stack exchange,提问作者Shubham Kaushik
相关产品推荐
相关产品推荐

