İçeriğe geç
Muhammet Şafak
Web Geliştirme 4 dk okuma

Laravel ile dosya yükleme ve görsel işleme

Laravel'de dosya yükleme, doğrulama ve görseli yeniden boyutlandırma adımlarını kapsayan uçtan uca akışı anlatıyorum.


Dosya yükleme, yüzeysel bakınca basit görünen ama ayrıntılara inince birkaç soruyu birden cevaplamayı gerektiren bir özellik: Dosya gerçekten yüklendi mi? Boyut ve tür sınırı ne olmalı? Aynı adda dosya varsa ne yapılacak? Yüklenen görsel profil fotoğrafıysa, uygun boyuta getirilmesi lazım.

Bu yazıda bu soruları Laravel’de nasıl çözdüğümü aktarıyorum. Basit bir profil fotoğrafı yükleme akışı üzerinden gideceğim.

Form tarafı

Dosya yüklemek için HTML formunun enctype="multipart/form-data" ile tanımlanmış olması gerekiyor:

<form action="/profil/fotograf" method="POST" enctype="multipart/form-data">
    {{ csrf_field() }}
    <input type="file" name="fotograf" accept="image/*">
    <button type="submit">Yükle</button>
</form>

enctype niteliğini unutmak bu özelliğin en sık karşılaşılan hatasıdır. Formun geri kalanı düzgün çalışıyor gibi görünür ama $request->file('fotograf') her zaman null döner. Form HTML’ini gözden geçirirken bu niteliği kontrol etmek ilk adım olmalı.

Doğrulama

Controller’da ilk adım daima doğrulama. Laravel’in image kuralı dosyanın gerçekten bir görsel olup olmadığını kontrol ediyor; max kuralı kilobayt cinsinden boyut sınırı koyuyor:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class ProfilController extends Controller
{
    public function fotografGuncelle(Request $request)
    {
        $this->validate($request, [
            'fotograf' => 'required|image|mimes:jpeg,png,gif|max:2048',
        ]);

        // Buradan sonra dosyanın geçerli olduğunu biliyoruz
        $dosya = $request->file('fotograf');

        // ...
    }
}

mimes:jpeg,png,gif kuralı MIME tipini kontrol ediyor — yalnızca uzantıya bakılmıyor, dosya içeriği de inceleniyor. max:2048 ile 2 MB sınırı koyuyorum.

MIME tipi kontrolünün neden önemli olduğunu şöyle açıklayayım: bir kullanıcı .jpg uzantısına <?php echo shell_exec($_GET['cmd']); ?> içerikli bir dosya yükleyebilir. Uzantıya bakarsanız geçti diyebilirsiniz. Ama Laravel’in mimes kuralı dosyanın ilk birkaç baytını okuyup gerçekten bir JPEG mi diye bakıyor. Güvenli yükleme için bu kontrol önemli.

Dosyayı kaydetmek

Laravel’in dosya sistemi API’si ile kaydetmek oldukça temiz:

// Benzersiz bir dosya adı üret ve public diskine kaydet
$dosyaAdi = uniqid() . '.' . $dosya->getClientOriginalExtension();
$yol = $dosya->storeAs('profil-fotograflari', $dosyaAdi, 'public');

// $yol: "profil-fotograflari/abc123.jpg" gibi bir değer

storeAs metodu, dosyayı istenen klasöre belirtilen adla kaydediyor. public diski config/filesystems.php içinde tanımlı; genellikle storage/app/public klasörüne yazıyor.

storage:link komutuyla oluşturulan sembolik bağ sayesinde bu dosyalar public/storage üzerinden erişilebilir oluyor.

uniqid() yerine daha güçlü bir benzersiz isim üretmek için Str::uuid() ya da Str::random(40) kullanılabilir. uniqid() mikrosaniye bazlı çalışıyor; eşzamanlı yüklemelerde çakışma ihtimali düşük ama teorik olarak mümkün. Büyük ölçekli uygulamalarda UUID veya rastgele string daha güvenli tercih.

Görseli yeniden boyutlandırmak

Profil fotoğraflarında genellikle standart bir boyut istiyorum. Bunun için Intervention Image kütüphanesini kullanıyorum:

composer require intervention/image

Dosyayı kaydetmeden önce yeniden boyutlandırıyorum:

use Intervention\Image\ImageManagerStatic as Image;

public function fotografGuncelle(Request $request)
{
    $this->validate($request, [
        'fotograf' => 'required|image|mimes:jpeg,png,gif|max:2048',
    ]);

    $dosya    = $request->file('fotograf');
    $dosyaAdi = uniqid() . '.jpg';
    $hedefYol = storage_path('app/public/profil-fotograflari/' . $dosyaAdi);

    // Görseli oku, yeniden boyutlandır, JPEG olarak kaydet
    Image::make($dosya->getRealPath())
        ->fit(200, 200)      // 200x200 kare, ortalanarak kırpıyor
        ->save($hedefYol, 80); // 80 kalite JPEG

    // Kullanıcı kaydını güncelle
    auth()->user()->update([
        'fotograf' => 'profil-fotograflari/' . $dosyaAdi,
    ]);

    return back()->with('basarili', 'Profil fotoğrafınız güncellendi.');
}

fit(200, 200) görseli 200x200 boyutuna, orantısız kırpma yapmadan sığdırıyor — kare profil fotoğrafları için ideal. save($yol, 80) 80 kalitede JPEG olarak kaydediyor; bu, boyut ile görsel kalite arasında makul bir denge.

Tüm yüklenen görselleri JPEG’e dönüştürmenin bir avantajı daha var: PNG formatında şeffaf arka planı olan bir görsel profil fotoğrafı olarak yüklenirse, siyah arka planla kaydedilebilir (JPEG saydamlığı desteklemez). Bu kenar durumu erken fark edilmezse renk uyumsuzluğu olarak kullanıcı şikayeti olarak geri döner. Şeffaflık içeren görseller için PNG olarak kaydetmek ya da arka plan rengini beyaz olarak doldurmak gerekiyor.

Eski fotoğrafı silmek

Güncelleme yapıldığında eski dosyayı silmek iyi bir alışkanlık:

use Illuminate\Support\Facades\Storage;

$eskiFotograf = auth()->user()->fotograf;

if ($eskiFotograf && Storage::disk('public')->exists($eskiFotograf)) {
    Storage::disk('public')->delete($eskiFotograf);
}

// Yeni fotoğrafı kaydet...

Eski dosyayı silmemek depolama sorununa yol açıyor. Kullanıcı her ay fotoğrafını değiştirirse yıl sonunda onlarca gereksiz dosya birikiyor. Bu önemsiz görünüyor başta; ama uygulamanın kullanıcı sayısı arttıkça disk kullanımı öngörülemeyen hızda büyüyor.

Hata durumlarını düşünmek

validate() başarısız olursa Laravel otomatik olarak form sayfasına yönlendiriyor ve hata mesajlarını $errors değişkenine aktarıyor. Görsel işleme sırasında bir sorun çıkarsa ise Intervention Image bir istisna fırlatıyor; bunu da Laravel’in genel hata işleyicisi karşılıyor.

Üretime alınmış bir uygulamada bu istisnaları daha özenle yakalamak ve kullanıcıya anlamlı bir hata mesajı göstermek daha doğru. Ama gelişme aşamasındaki bir proje için şu an bu düzey yeterli.

Bir tuzak daha: PHP’nin php.ini dosyasındaki upload_max_filesize ve post_max_size değerleri, Laravel’in max doğrulama kuralından önce devreye giriyor. Laravel max:2048 diyebilir; ama php.ini’de upload_max_filesize=1M yazıyorsa 1.5 MB’lık bir dosya Laravel’e hiç ulaşmadan reddediliyor. Bu durumda Laravel hata mesajı yerine PHP’nin ham hata sayfası görünebilir. Geliştirme ortamında bu değerleri kontrol etmek, beklenmedik davranışların kaynağını bulmayı kolaylaştırıyor.

Dosya yükleme işlemi bu kadar uzun sürmesine gerek yok gibi görünebilir, ama her adımı atlamak ileride sorun çıkarıyor. Doğrulamayı atlamak zararlı dosyaların yüklenmesine, boyutlandırmayı atlamak depolama sorunlarına, eski dosyayı silmemeyi atlamak birikim sorunlarına yol açıyor. Bu adımları başından kurmak sonradan düzeltmekten kolay.

Etiketler: #Laravel
Paylaş:

İlgili Yazılar

Sitede Ara

Yazı, proje ve sayfalarda arama yapmak için yazmaya başlayın.

Esc ile kapat Pagefind ile güçlendirildi