Laravel项目中Summernote附件存AWS S3遇CORS问题求解
解决Laravel+Summernote上传附件到AWS S3的CORS问题
1. 配置AWS S3的CORS规则
CORS错误的核心原因是S3存储桶未允许你的Laravel域名发起跨域请求,直接在AWS控制台修改存储桶的CORS配置:
- 登录AWS控制台,进入目标S3存储桶的「权限」标签
- 找到「跨域资源共享(CORS)」,替换为以下规则(根据实际环境修改域名):
[ { "AllowedHeaders": [ "*" ], "AllowedMethods": [ "GET", "PUT", "POST", "DELETE" ], "AllowedOrigins": [ "http://localhost:8000", // 本地开发域名 "https://your-production-domain.com" // 生产环境域名 ], "ExposeHeaders": [ "ETag" ] } ]
注意:生产环境尽量指定具体域名,避免使用
*;AWS配置生效可能需要1-2分钟,修改后稍等再测试。
2. Laravel端签名与上传逻辑
2.1 配置S3连接
确保.env文件中AWS配置正确:
AWS_ACCESS_KEY_ID=你的AWS访问密钥ID AWS_SECRET_ACCESS_KEY=你的AWS秘密访问密钥 AWS_DEFAULT_REGION=你的存储桶区域(如us-east-1) AWS_BUCKET=你的存储桶名称 AWS_URL=https://你的存储桶访问域名(可选,如CloudFront域名) AWS_USE_PATH_STYLE_ENDPOINT=false
2.2 编写签名路由与控制器
在routes/web.php添加签名接口路由:
Route::post('/summernote/sign-s3', [SummernoteController::class, 'signS3'])->name('summernote.sign-s3');
创建SummernoteController并实现签名逻辑:
<?php namespace App\Http\Controllers; use Illuminate\Http\Request; use Illuminate\Support\Facades\Storage; class SummernoteController extends Controller { public function signS3(Request $request) { $file = $request->file('file'); $s3 = Storage::disk('s3'); $client = $s3->getDriver()->getAdapter()->getClient(); // 生成唯一文件路径,避免重名 $fileKey = 'summernote-uploads/' . uniqid() . '-' . $file->getClientOriginalName(); // 创建S3预签名上传请求 $command = $client->getCommand('PutObject', [ 'Bucket' => config('filesystems.disks.s3.bucket'), 'Key' => $fileKey, 'ContentType' => $file->getMimeType(), 'ACL' => 'public-read', // 根据需求设置,如private需配合签名访问 ]); $presignedRequest = $client->createPresignedRequest($command, '+15 minutes'); return response()->json([ 'presigned_url' => (string)$presignedRequest->getUri(), 'public_url' => $s3->url($fileKey) ]); } }
确保IAM用户拥有S3的
PutObject权限,可给用户附加AmazonS3FullAccess或更精细的权限策略。
3. Summernote前端配置
修改Summernote初始化代码,替换默认上传逻辑为S3上传:
$(document).ready(function() { $('#summernote').summernote({ height: 350, callbacks: { // 处理图片上传 onImageUpload: function(files) { uploadToS3(files[0], 'image'); }, // 处理视频上传 onMediaUpload: function(files) { uploadToS3(files[0], 'video'); } } }); function uploadToS3(file, type) { const formData = new FormData(); formData.append('file', file); // 第一步:获取S3预签名URL $.ajax({ url: '{{ route('summernote.sign-s3') }}', method: 'POST', headers: { 'X-CSRF-TOKEN': '{{ csrf_token() }}' }, data: formData, processData: false, contentType: false, success: function(res) { // 第二步:通过预签名URL上传文件到S3 $.ajax({ url: res.presigned_url, method: 'PUT', data: file, contentType: file.type, processData: false, success: function() { // 上传成功后插入到编辑器 if (type === 'image') { $('#summernote').summernote('insertImage', res.public_url); } else { $('#summernote').summernote('insertVideo', res.public_url); } }, error: function(xhr) { console.error('S3上传失败:', xhr.responseText); } }); }, error: function(xhr) { console.error('获取签名失败:', xhr.responseText); } }); } });
4. 常见问题排查
- 仍报CORS错误:检查S3的
AllowedOrigins是否与请求域名完全匹配(包括HTTP/HTTPS、端口号),等待AWS配置同步完成。 - 预签名上传失败:检查签名中的
ContentType是否与文件实际MIME类型一致,以及IAM用户的S3权限是否足够。 - Laravel报419错误:确保前端请求带上了CSRF令牌,或者将签名路由加入
VerifyCsrfToken白名单(不推荐)。
内容的提问来源于stack exchange,提问作者Theodory
相关产品推荐
相关产品推荐

