Laravel Livewire上传文件至Minio失败求助
问题描述
Laravel 7项目中正常运行的Minio文件上传方法,在Laravel 9的Livewire项目中失效。文件能成功保存到本地storage/app/proof目录,但无法上传至Minio,报错如下:
file_get_contents(C:\Users\akobo\AppData\Local\Temp\phpFA54.tmp): Failed to open stream: No such file or directory
相关代码:
Controller代码
<?php namespace App\Http\Livewire; use Livewire\Component; use Livewire\WithFileUploads; use App\Models\AlmProofs; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Storage; class DocumentationProof extends Component { use WithFileUploads; public $photo; public $modalplugin = false; protected $rules = [ 'photo' => 'mimes:png,pdf', ]; public function submitForm() { $this->validate(); $files = $this->photo; $originalName = $files->getClientOriginalName(); $fileName = time() . '-' . $originalName; $details = AlmProofs::where('users_id', Auth::user()->id)->first(); if (isset($details)) { $details->delete(); } AlmProofs::create([ 'users_id' => Auth::user()->id, 'ori_name' => $originalName, 'file_name' => $fileName, 'web_path' => "alumni/".$fileName, ]); $this->photo->storeAs('proof', $fileName); Storage::disk('minio')->put("/alumni/".$fileName, file_get_contents($files)); $this->modalplugin = true; } public function render() { return view('livewire.documentation-proof')->layout('layouts.management'); } }
Blade视图代码
<x-slot name="header"> {{ __('Documentation Proof') }} </x-slot> <div x-data="employment" class="min-h-full overflow-hidden bg-white p-6 shadow-xl md:rounded-lg" x-init="$watch('modalAlpine', function(value, oldValue) { modal_checkbox.checked = value; if (modal_checkbox.checked) { bgOverlay.classList.remove('hidden'); } else { bgOverlay.classList.add('hidden'); } })"> <h1 class="font-bold">Documentation Proof</h1> <div class="mx-auto max-w-md overflow-hidden rounded-xl bg-white px-6 py-4 shadow-md md:max-w-4xl"> <div class="md:flex"> <div class="mt-6 w-full overflow-hidden"> <x-jet-validation-errors class="mb-4" /> <form wire:submit.prevent="submitForm"> <div> @if (session()->has('message')) <div class="alert alert-success mb-4 text-orange-500"> <i class="fas fa-check-circle"></i> {{ session('message') }} </div> @endif </div> <div class="flex flex-col sm:flex-row sm:space-x-3"> <div class="w-full"> <label for="remark" class="font-small text-sm text-gray-700"> Please upload one file only - ie.Certificate of Completion </label> <input type="file" wire:model="photo" class="block w-full"> <label for="remark" class="font-small mt-1 block w-full rounded-md border-gray-300 text-sm text-red-500"> File format: .png/.pdf only</label> {{-- {{ $proofFile }} --}} </div> </div> <div class="mt-4 flex justify-end space-x-3"> <div class="text-sm text-red-600"> {{ __('Please click save button after every update') }} </div> <div class=""> <button class="rounded-md border border-transparent bg-orange-400 px-4 py-2 text-xs font-semibold uppercase tracking-widest text-white transition hover:bg-orange-600 focus:border-gray-900 focus:outline-none focus:ring focus:ring-gray-300 active:bg-gray-900 disabled:opacity-25"> Save </button> </div> </div> </form> <input id="modal-checkbox" type="checkbox" class="peer hidden" /> <div class="top-50 sm: absolute top-1/2 left-1/2 z-10 w-full -translate-x-1/2 -translate-y-1/2 scale-0 transform px-2 opacity-0 transition-all duration-300 peer-checked:scale-100 peer-checked:opacity-100 md:w-1/4"> <livewire:intec-modal-plugin> </div> </div> </div> </div> <script> const modal_checkbox = document.getElementById('modal-checkbox'); const bgOverlay = document.getElementById('bg-overlay'); document.addEventListener('alpine:init', () => { Alpine.data('employment', () => ({ modalAlpine: @entangle('modalplugin'), toggle() { modal_checkbox.checked = ! modal_checkbox.checked; this.modalAlpine = modal_checkbox.checked; if (modal_checkbox.checked) { bgOverlay.classList.remove('hidden'); } else { bgOverlay.classList.add('hidden'); } }, })) }) </script> </div>
filesystems.php配置
<?php return [ 'default' => env('FILESYSTEM_DISK', 'local'), 'disks' => [ 'local' => [ 'driver' => 'local', 'root' => storage_path('app'), ], 'public' => [ 'driver' => 'local', 'root' => storage_path('app/public'), 'url' => env('APP_URL').'/storage', 'visibility' => 'public', ], 'minio' => [ 'driver' => 's3', 'endpoint' => env('MINIO_ENDPOINT', '(url)'), 'url' => env('MINIO_ENDPOINT', '(url)'), 'use_path_style_endpoint' => true, 'key' => env('AWS_KEY'), 'secret' => env('AWS_SECRET'), 'region' => env('AWS_REGION'), 'bucket' => env('AWS_BUCKET'), 'visibility' => 'public' ], 's3' => [ 'driver' => 's3', 'key' => env('AWS_ACCESS_KEY_ID'), 'secret' => env('AWS_SECRET_ACCESS_KEY'), 'region' => env('AWS_DEFAULT_REGION'), 'bucket' => env('AWS_BUCKET'), 'url' => env('AWS_URL'), ], ], ];
解决方案
问题根源是:调用$this->photo->storeAs()之后,Livewire会自动清理临时上传文件,导致后续file_get_contents($files)找不到原临时文件路径。
提供两种可行修改方案:
方案一:先读取文件内容再执行存储操作
在保存到本地前先获取文件内容,再分别同步到本地和Minio:
public function submitForm() { $this->validate(); $files = $this->photo; $originalName = $files->getClientOriginalName(); $fileName = time() . '-' . $originalName; $details = AlmProofs::where('users_id', Auth::user()->id)->first(); if (isset($details)) { $details->delete(); } AlmProofs::create([ 'users_id' => Auth::user()->id, 'ori_name' => $originalName, 'file_name' => $fileName, 'web_path' => "alumni/".$fileName, ]); // 先获取文件内容(推荐用Laravel文件对象自带方法) $fileContent = $files->getContent(); // 保存到本地 $this->photo->storeAs('proof', $fileName); // 上传到Minio Storage::disk('minio')->put("/alumni/".$fileName, $fileContent); $this->modalplugin = true; }
方案二:从本地存储读取已保存文件上传到Minio
如果已经完成本地存储,直接读取本地文件路径上传:
public function submitForm() { $this->validate(); $files = $this->photo; $originalName = $files->getClientOriginalName(); $fileName = time() . '-' . $originalName; $details = AlmProofs::where('users_id', Auth::user()->id)->first(); if (isset($details)) { $details->delete(); } AlmProofs::create([ 'users_id' => Auth::user()->id, 'ori_name' => $originalName, 'file_name' => $fileName, 'web_path' => "alumni/".$fileName, ]); // 保存到本地并获取存储路径 $localPath = $this->photo->storeAs('proof', $fileName); // 从本地存储读取文件上传到Minio Storage::disk('minio')->put("/alumni/".$fileName, Storage::get($localPath)); $this->modalplugin = true; }
额外注意事项
- 确保
.env文件中Minio相关配置(MINIO_ENDPOINT、AWS_KEY、AWS_SECRET、AWS_BUCKET)正确无误 - 若上传大文件,需检查Livewire和PHP的上传大小限制配置
内容的提问来源于stack exchange,提问作者zerhez
相关产品推荐
相关产品推荐

