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

如何通过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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 14:50:22