Laravel项目中如何在邮件中使用AWS S3非公开图片
问题描述
我有一个基于Laravel的项目,使用Illuminate\Support\Facades\Mail发送邮件,纯文本邮件发送正常。项目部署在AWS上,图片存储在非公开的AWS S3存储桶中,现在需要在邮件中嵌入这些S3里的图片。
当前邮件构建代码如下:
public function build() { $this->subject('You have missed an interaction scheduled for ' . $this->schedule->clientname); return $this->view('emails.scheduleMissedCheckinToWorker') ->replyTo( $this->schedule->schedulerEmail, $this->schedule->schedulername ) ->with([ 'schedule' => $this->schedule, ]); }
我已获取到图片在AWS的完整路径,但尝试了ChatGPT提供的方案后无效,该方案代码如下:
public function build() { // Retrieve the image from S3 $imageData = Storage::disk('s3')->get($this->missedSchedule->worker_image_path); return $this->view('emails.scheduleMissedCheckin') ->subject('An interaction you scheduled for ' . $this->missedSchedule->clientname . ' was not checked into by ' . $this->missedSchedule->workername) ->replyTo($this->missedSchedule->workerEmail, $this->missedSchedule->workername) ->with(['missedSchedule' => $this->missedSchedule]) ->attachData($imageData, 'worker.jpg', [ 'mime' => 'image/jpeg', 'as' => 'worker.jpg', 'content_id' => 'workerImage' ]) ->withSwiftMessage(function ($message) { $message->embedFromPath('worker.jpg', 'workerImage'); }); }
Blade视图中使用:
<img src="cid:workerImage" alt="Worker Image" width="200" height="200">
需要有效的解决方法。
解决方案
方法一:使用Laravel邮件内置的embedData方法(推荐)
ChatGPT方案的核心问题是同时调用了attachData和embedFromPath,导致CID冲突。正确的做法是直接通过embedData生成嵌入图片的CID,再传递到视图:
public function build() { // 从S3获取图片原始数据 $imageData = Storage::disk('s3')->get($this->schedule->worker_image_path); // 动态获取图片MIME类型,避免硬编码错误 $mimeType = Storage::disk('s3')->mimeType($this->schedule->worker_image_path); // 生成嵌入图片的CID $imageCid = $this->embedData($imageData, 'worker-image.jpg', [ 'mime' => $mimeType ]); $this->subject('You have missed an interaction scheduled for ' . $this->schedule->clientname); return $this->view('emails.scheduleMissedCheckinToWorker') ->replyTo($this->schedule->schedulerEmail, $this->schedule->schedulername) ->with([ 'schedule' => $this->schedule, 'workerImageCid' => $imageCid // 将CID传递到视图 ]); }
在Blade视图中引用:
<img src="{{ $workerImageCid }}" alt="Worker Image" width="200" height="200">
方法二:生成S3临时公开URL
如果不想将图片作为邮件附件发送(可减少邮件体积),可以生成S3的预签名临时URL,有效期可自定义:
public function build() { // 生成有效期1小时的S3临时URL $imageUrl = Storage::disk('s3')->temporaryUrl( $this->schedule->worker_image_path, now()->addHour() ); $this->subject('You have missed an interaction scheduled for ' . $this->schedule->clientname); return $this->view('emails.scheduleMissedCheckinToWorker') ->replyTo($this->schedule->schedulerEmail, $this->schedule->schedulername) ->with([ 'schedule' => $this->schedule, 'workerImageUrl' => $imageUrl ]); }
Blade视图中引用:
<img src="{{ $workerImageUrl }}" alt="Worker Image" width="200" height="200">
方法三:修复ChatGPT的方案(不推荐)
如果要保留类似ChatGPT的写法,只需去掉重复的embedFromPath调用,attachData已经通过content_id设置了CID:
public function build() { $imageData = Storage::disk('s3')->get($this->missedSchedule->worker_image_path); $mimeType = Storage::disk('s3')->mimeType($this->missedSchedule->worker_image_path); return $this->view('emails.scheduleMissedCheckin') ->subject('An interaction you scheduled for ' . $this->missedSchedule->clientname . ' was not checked into by ' . $this->missedSchedule->workername) ->replyTo($this->missedSchedule->workerEmail, $this->missedSchedule->workername) ->with(['missedSchedule' => $this->missedSchedule]) ->attachData($imageData, 'worker.jpg', [ 'mime' => $mimeType, 'content_id' => 'workerImage' // 仅保留此处设置CID ]); }
视图保持不变:
<img src="cid:workerImage" alt="Worker Image" width="200" height="200">
注意事项
- 确保Laravel的S3磁盘配置正确,拥有目标图片的读取权限;
- 方法一中的
embedData会将图片作为邮件内嵌附件发送,适合小尺寸图片; - 方法二中的临时URL需要确保邮件接收方能访问AWS S3公网地址,适合大尺寸图片;
- 避免硬编码MIME类型,建议使用
Storage::disk('s3')->mimeType()动态获取。
内容的提问来源于stack exchange,提问作者user761100
相关产品推荐
相关产品推荐

