İçeriğe geç

Medya ve depolama

Medya, bir CMF'nin en çok saldırıya uğrayan yüzeylerinden biridir: kullanıcı dosya yükler ve sunucu onu sunar. CPalius yükleme hattını tek bir kapıya bağlar ve her adımı sertleştirir.

Yükleme: tek kapı

Hiçbir kod doğrudan dosya yazmaz; AssetManager::upload() yazma için tek geçittir. Her yükleme şu denetimlerden geçer:

  1. MIME doğrulaması: tür dosya uzantısına değil, dosyanın içeriğine (finfo) bakılarak belirlenir.
  2. İzin listesi: MimeTypeAllowlist çekirdeğe gömülüdür; modüller bu listeyi genişletemez.
  3. Uzantı türetme: diskteki uzantı, istemcinin gönderdiği dosya adından değil, doğrulanmış MIME'dan sunucuda türetilir. evil.php.jpg adıyla gelen bir dosya jpg olarak, rastgele adla saklanır.
  4. İçerik özeti (hash) ile tekilleştirme: aynı içerik ikinci kez yüklenirse aynı varlık yeniden kullanılır.
  5. Çalıştırılamayan klasör: dosyalar public/uploads altına yazılır ve oradaki .htaccess katmanlı bir korumayla betik çalıştırmayı, MIME sezmeyi ve içerik gömmeyi engeller.

İzinli türler

Aile Türler
Görsel JPEG, PNG, GIF, WebP, AVIF, BMP, TIFF, HEIC/HEIF, ICO
Belge Yalnızca PDF
Video MP4, WebM, OGG video, QuickTime (MOV), Matroska (MKV)
Ses Bilerek dışarıda: haritaya açıkça eklenene kadar

SVG, HTML, arşivler ve application/octet-stream bilinçli olarak dışarıda tutulur. SVG içinde betik taşıyabilir, HTML sayfa gibi çalışır, arşivler içerik denetimini atlatır.

Ek tür gerekirse

İzin listesi çekirdeğe aittir. Bir modül kendi çağrısında daralma talep edebilir (restrictedTo([...]), $allowedMimePrefixes, $maxBytes); izin listesini genişletemez.

Medya kütüphanesi

Media modülü (Studio → Medya) yüklenen dosyaları listeler, arar ve medya seçici sunar. Seçici diğer ekranlardan çağrılır:

// tek seçim
window.CPaliusMediaPicker.open((asset) => { /* asset.id, asset.url, asset.originalName */ });

// çoklu seçim
window.CPaliusMediaPicker.open((assets) => assets.forEach(add), { multiple: true });

Bir varlığın önizleme bilgisi için: GET /admin/media/api/assets/{id}/meta → {id, url, name, mime}.

Görsel/dosya alanları bu seçiciyi field-media betiğiyle otomatik kullanır; bkz. Alan sistemi.

Görsel küçük resimleri

ImageProcessor, istenen boyutta görseli ilk istekte üretir ve public/uploads/cache/ altına yazar. Sonraki her istekte dosyayı doğrudan web sunucusu sunar, PHP devreye girmez. Modlar kırpma (crop) ve sığdırma biçimindedir.

Şablonda cp_thumb süzgeci kullanılır: {{ asset|cp_thumb(400, 300) }} (varsayılan kırpma) ya da {{ asset|cp_thumb(400, 300, 'fit') }}. Kaynak olarak varlık nesnesi, kimliği ya da yol verilebilir. Varlık silinince türevler purge ile temizlenir.

CDN

CDN anahtarı çok basit bir sözleşme izler: cdn.base_url yalnızca /uploads önekinin yerine geçer.

Kurulum cdn.base_url
Sizin sunucunuzun önünde bir çekme bölgesi (pull-zone) https://cdn.example.com/uploads
Herkese açık kova (R2 / S3 / Spaces) https://pub-xxxx.r2.dev

CDN anahtarı uzak depolamadan bağımsızdır. Çekme bölgesi hiçbir yükleme gerektirmez: önbellekte olmayan dosyayı sizin sunucunuzdan çeker. Kova zorunluluğu koşmak, en ucuz ve en yaygın kurulumu ifade edilemez bırakırdı. cdn.images_only (varsayılan açık) yalnızca görsellerin CDN'den sunulmasını sağlar.

Dosya adresleri her zaman AssetUrlGenerator ile üretilir; modülünüzde "/uploads/".$key yazmayın.

Uzak depolama: S3, R2, FTP

AACP → Bakım → Depolama ekranında üç hedef tanımlanabilir:

Hedef Ayarlar (öne çıkanlar)
S3 uyumlu endpoint, region (varsayılan eu-central-1), bucket, anahtar/gizli anahtar, prefix, path_style
Cloudflare R2 region (auto), bucket, anahtar/gizli anahtar, prefix
FTP host, port (21), kullanıcı/parola, path, pasif mod, SSL, zaman aşımı

Hedef arayüzü bilerek minimaldir: put, has, delete, test. CPalius uzaktan okumaz; bu, "uzak dosya her istekte okunabilir olsun" bağımlılığını doğurmamak için bilinçli bir karardır.

Canlı yazma testi

Bir hedef, şu anki yapılandırmayla canlı bir yazma sınaması geçmeden kullanılmaz. Makul görünen kimlik bilgileri kanıt değildir: yalnız ListBucket izni veren ama PutObject'u yasaklayan bir kova politikası, salt okunur ev dizinli bir FTP hesabı ya da yanlış yazılmış bir uç nokta, ancak dosya yazıp geri okuyan bir testle yakalanır. Kaydet ve sına düğmesi bunu yapar; sonuç saklanır.

Medya kopyalama (offload)

Uzak depolama açılırsa yüklenen medya uzak hedefe kopyalanır (taşınmaz):

  • Yerel dosya kalır ve asıl kopya olarak kalır. Görsel işleme GD ile diskten okur; uzak asıl dosya, her küçük resim üretiminde ağdan indirmek demek olurdu.
  • public/uploads/.htaccess korumaları baytlarla birlikte kovaya taşınmaz; orada koruma, operatörün yönettiği bir kova politikasıdır.
  • Dosya yedeği medyayı içermez hale gelmesin diye yerel kopya silinmez.

Sonuç: uzak kopya bir teslimat optimizasyonudur, taşınma değil. Bu yüzden her hata kurtarılabilirdir: offload() istisna fırlatmaz, bool döner; bir S3 kesintisi çalışan bir görsel yüklemesini 500'e çevirmez.

Ayar Varsayılan
storage.media.sweep_enabled kapalı
storage.media.sweep_batch 200

sweep zamanlanmış görevi (core.media_offload_sweep, günlük 03:40) henüz uzağa gitmemiş dosyaları toplu olarak gönderir; ilk açılışta mevcut kütüphaneyi taşımanın yoludur.

Yedekler için depolama

Aynı hedefler yedek gönderimi için de kullanılır; ayrıntı ve farklı davranış (orada hata gürültülüdür) için bkz. Yedekleme ve bakım.

Modül yazarı için

// Yükleme: tek yetkili kapı
$asset = $assetManager->upload($uploadedFile, allowedMimePrefixes: ['image/'], maxBytes: 5 * 1024 * 1024);

// Adres: her zaman jeneratör
$url = $assetUrlGenerator->forKey($asset->getStorageKey());
  • Yüklemeyi kendiniz yazmayın, AssetManager kullanın.
  • Dosya türü kısıtını çağrıda daraltın (image/ gibi).
  • Dosya adı olarak kullanıcının gönderdiğini kullanmayın; originalName yalnızca görüntü içindir.
  • Adres için AssetUrlGenerator, boyut için küçük resim yardımcısı kullanın.
Son güncelleme: 13.10.2026