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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 11:51:58