Manipulace s obrázky [Nový ImageStorage]
- CZechBoY
- Člen | 3608
@JanMikeš Nebude to spíš problém pojmenování? Furt si můžu
udělat konkrétní aliasy CommentUserIcon
a vím o co jde.
To, že někde budou miniatury je jasný už skoro od počátku projektu (pokud
se obrázek dá nějak uživatelsky nahrát) takže alias vytvoříš hned.
Editoval CZechBoY (1. 2. 2017 1:00)
- Martk
- Člen | 661
@JanMikeš Napadlo mě udělat parametrizované aliasy, aby se zamezilo duplicitě.
images:
local:
aliases:
resizeExact: "resize:$1,$2"
{img "image.jpg", "resizeExact:150,150"}
Originální cesta je original/image.jpg
A nejspíše by se vytvořila tato cesta resizeExact/150.150/image.jpg
Tady by mohl nastat velký nepořádek v případě mnohonásobných namespace, řešením by bylo mít jen jedno namespace nebo převést namespace1/namespace2 na složku namespace1.namespace2 a nebo si dávat pozor na název aliasů a namespace.
Aliasy chci zachovat, protože u složitějších funkcí, zvláště u cloudinary a jejich vnořených funkcí, je velmi těžké generovat správný název složky.
Editoval Martk (1. 2. 2017 12:47)
- mordare
- Začátečník | 117
Zdravím mám pár dotazů.
Při použití makra {img} se mi nevykreslí obrázek, ale vždy se vypíše
jen cesta k němu. např.:
Při zápisu
{img 'namespace/'.$presenter->getUser()->getId().'/logo.jpg'}
se do stránky vykreslí jen text: data/namespace/1505/original/logo.jpg
Při použití attributu n:img
se obrázek vykreslí, ale zaboha
nemůžu přijít na to jak tam dostat helpery. Když sem postupoval podle
návodu na GITu s přidáním noImage
Dostanu chybovou
hlášku:
syntax error, unexpected ':'
V šabloně mám
<img n:img="'namespace/'.$presenter->getUser()->getId().'/logo.jpg'|noImage:'noimage.jpg'">
Osobně bych radši používal Makro než atribut n:img. Jak to teda zprovoznit? Případně jak aspoň zprovoznit ty Helpery při použití atributu?
Jak v presenteru smazat obrázek. Přez použitou Traitu TPresenter, která zpřístupňuje ImageFacede se mi to nepovedlo. Rozjel sem to následujícím způsobem, ale nepřijde mi že by to bylo uplně správně:
$this->storage->delete($this->storage->createResource('namespace/'.$this->getUser()->getId().'/'.$photo));
ps.: verzi používám @dev
Editoval mordare (8. 2. 2017 10:00)
- Jan Mikeš
- Člen | 771
@mordare makro {img}
ti nevytvoří tag
<img>
„pouze“ vypíše tu cestu, což sám popisuješ,
správně tedy je:
<img src="{img ...}">
Ps. pokud by makro vykreslovalo i tag <img>
, bylo by to
pak nepoužitelné například
pro styles="background: url({img ...})"
Editoval Jan Mikeš (8. 2. 2017 13:36)
- Martk
- Člen | 661
@mordare Přemýšlel jsem spíše, jestli mám v tomto balíčku podporovat i jiné úložiště než lokální. Pokud používáš nejnovější verzi, tak tam není nic na pevno. Stačí si vytvořit novou třídu a tu podědit od WebChemistry\Images\Storage.
Základní šablona asi tady:
<?php
namespace WebChemistry\Images\Storages;
use WebChemistry\Images\Modifiers\ModifierContainer;
use WebChemistry\Images\Resources\IFileResource;
use WebChemistry\Images\Resources\IResource;
use WebChemistry\Images\Resources\Transfer\ITransferResource;
use WebChemistry\Images\Storage;
use WebChemistry\Images\Storages\Cloudinary\CloudinaryFacade;
class CloudinaryStorage extends Storage {
private $modifierContainer;
public function __construct(ModifierContainer $modifierContainer, array $config) {
$this->modifierContainer = $modifierContainer;
}
public function link(IFileResource $resource) {
// vsude budes potrebovat asi tohle
$resource->getId(); // identifikator obrazku
$resource->getAlises(); // pole aliasu
$this->modifierContainer->modifiersFromResource($resource); // ktere modifikace po nas uzivatel chce, extrahovany z aliasu
}
public function save(IResource $resource) {
if (!$resource instanceof ITransferResource) {
return $resource; // or throw exception?
}
$resource->setSaved();
return; // Vratit IFileResource
}
public function copy(IFileResource $src, IFileResource $dest) {
}
public function move(IFileResource $src, IFileResource $dest) {
}
public function delete(IFileResource $resource) {
}
}
Předpokládám, že všechny změny probíhají na externím serveru, jinak by to bylo o něco málo složitější.
- mordare
- Začátečník | 117
Jo našel sem Interface a díval sem se že by to neblo ani těžké vytvořit.
Ale potom když sem se díval do DI extension soboru, který se stará o načtení konfigurace z Neonu a zaregistrování služeb tak tam to máš vytvořeno načtení konfigu jen pro Local a Cloudinary napevno. Tudíž pokud bych si napsal svůj storage tak bych si musel psát i vlastní DI. Vím že to neni problém a bylo by to v podstatě zkopírování tvého souboru s dopsáním vlastní sekce. Ale právě to mi přijde jako zbytečnost abych kopíroval něco co už je jen kvůli tomu abych tam dopsal pár řádků.
- Rudolf247
- Člen | 38
Zdravím, já už vážně nevím jak dál.
S uploadem jsem neměl sebemenší problém, ale při vykreslování stále
dostávám hlášku „Namespace is not valid.“
Tak jsem si na basePresentru jeden obrázek přes
$this->storage->createResource() dumpnul a vážně nemá přidělený
žádný namespace (namespace ⇒ null).
Ale když si udělám dump po uploadu, tak obrázek namespace přidělený má.
Upload mám řešený přes jzechy/jQuery-FileUpload
public function save(\Nette\Http\FileUpload $file, array $params = []) {
$resource = $this->storage->createUploadResource($file);
$resource->setNamespace($params['namespace']);
$result = $this->storage->save($resource);
return $file->getSanitizedName();
}
Na šabloně potom
<img class="img img-fluid" src="{img 'ds/original/22467334-1984457505212953-7025201851310817024-o.jpg'}">
Děkuji za každou radu.
- Martk
- Člen | 661
Díky za report.
Špatně bude pravděpodobně řádek:
$resource->setNamespace($params['namespace']);
Namespace může obsahovat pouze tyto znaky / a-z A-Z _ 0–9
V šabloně by mělo stačit tohle:
<img class="img img-fluid" src="{img 'ds/22467334-1984457505212953-7025201851310817024-o.jpg'}">
original se použije sám, pokud nedáš jako 2 parametr alias.
Správný upload a vykreslení by měl vypadat takhle:
$result = $this->storage->save($resource);
$idToSave = (string) $result;
$db->updateRow(['image' => $idToSave]); // aktualizace řádku v databázi
a vykreslení v šabloně:
<img src="{img $idToSave}"> {* $idToSave = vytažení obrázku z databáze *}
- Ivorius
- Nette Blogger | 119
Martk napsal(a):
@Ivorius Nyní se to nastavuje v aliasech, bohužel se na to vytváří speciální složka, kterou ještě opravím.
images: local: aliases: article_default: 'defaultImage:noimage.jpg'
<img n:img="'image.jpg', article_default">
Mohl bys mi prosím ukázat, jak to tedy má vypadat, když chci zmenšené obrázky – mini a zároveň použít defaultní obrázek, pokud není nastaven?
aliases:
user_default: "defaultImage: noimage.jpg"
mini: "resize:30,30,exact|sharpen"
small: "resize:120,120,exact|sharpen"
<img n:img="'image.jpg', user_default,mini">
asi není to správné
- flamengo
- Člen | 135
Ahoj, rád bych zkusil novou verzi a tak bych se rád zeptal, jak mám
použít v komponentě. Vlastně stejný dotaz, jako tento https://forum.nette.org/…imagestorage?p=3.
Zkusil jsme místo WebChemistry\Images\AbstractStorage
použít
WebChemistry\Images\Template\ImageFacade
, ale potom mi to v latte
hází Notice Undefined variable: _imageFacade
.
Za odpověď předem díky.
- Martk
- Člen | 661
@flamengo V nejnovější verzi po smazání cache už není potřeba přidávat nic do šablony ani používat traitu TPresenter
@Ivorius Tvůj kód by měl v nejnovější verzi fungovat
<img n:img="'image.jpg', user_default,mini">
Nyní lze používat proměnné v aliasech:
aliases:
resize: 'resize:$1,$2,$3'
resizeExact: 'resize:$1,$2,exact'
resizeSquare: 'resize:$1,$1'
crop: 'crop:$1,$2,$3,$4'
resizeCrop: 'resize:$1,$2,exact|crop:$3,$4,$5,$6'
Použití:
{img $image, resize(20,30,shrink)}
{img $image, resizeExact(20,30)}
{img $image, resizeSquare(10), crop(5,5,5,5)}
- cafesk8
- Člen | 103
Zdravím,
máte někdo zkušenost s napojením na https://github.com/…y-FileUpload ? Ať dělám, co dělám, nedaří se mi to.
namespace App\Model;
use Nette\SmartObject;
use Nette\Utils\FileSystem;
use Zet\FileUpload\Model;
use WebChemistry\Images\IStorage;
class UploadFilesRepository implements \Zet\FileUpload\Model\IUploadModel {
use SmartObject;
/** @inject @var WebChemistry\Images\IStorage */
public $storage;
/** @inject @var Nette\Utils\Upload */
public $upload;
/**
* Zpracování požadavku o smazání souboru.
* @param $uploaded Hodnota navrácená funkcí save.
*/
public function remove($uploaded) {
}
/**
* Zpracování přejmenování souboru.
* @param $upload Hodnota navrácená funkcí save.
* @param $newName Nové jméno souboru.
* @return mixed Vlastní návratová hodnota.
*/
public function rename($upload, $newName) {
Nette\Utils\FileSystem::rename($upload,"../www/advert_pics/".$newName);
}
/**
* Uložení nahraného souboru.
* @param \Nette\Http\FileUpload $file
* @param array $params Pole vlastních hodnot.
* @return mixed Vlastní navrátová hodnota.
*/
public function save(\Nette\Http\FileUpload $file, array $params = []) {
// vytvorime zdroj pro obrazek
$resource = $this->storage->createUploadResource($upload);
// pridame namespace
$resource->setNamespace('namespace');
// ulozime
$result = $this->storage->save($resource);
// zobrazime url adresu
echo $this->storage->link($result);
}
Vyhazuje mi to: Call to a member function createUploadResource() on null
Je mi jasné tedy, že $this->storage je null, nevím si rady jak si předat ten soubor.
Díky
- cafesk8
- Člen | 103
Mysteria napsal(a):
Inject anotace fungují jenom v presenterech, předej si ty závislosti normálně přes konstruktor.
Díky. Bohužel ani to se mi nedaří, asi to dělám úplně špatně.
namespace App\Model;
use Nette\SmartObject;
use Nette\Utils\FileSystem;
use Zet\FileUpload\Model;
use WebChemistry\Images\IStorage;
class UploadFilesRepository implements \Zet\FileUpload\Model\IUploadModel {
use SmartObject;
public $storage;
public $upload;
public function __construct () {
$this->storage = new \WebChemistry\Images\IStorage;
$this->upload = new \Nette\Utils\Upload ;
}
# další metody třídy
}
vyhazuje mi to
Class 'WebChemistry\Images\IStorage' not found
- Martk
- Člen | 661
namespace App\Model;
use Nette\SmartObject;
use Nette\Utils\FileSystem;
use Zet\FileUpload\Model;
use WebChemistry\Images\IStorage;
class UploadFilesRepository implements \Zet\FileUpload\Model\IUploadModel {
use SmartObject;
public $storage;
public $upload;
public function __construct (IStorage $storage, Upload $upload) {
$this->storage = $storage;
$this->upload = $upload;
}
# další metody třídy
}
config.neon
services:
- App\Model\UploadFilesRepository
presenter:
class CustomPresenter {
/** App\Model\UploadFilesRepository @inject */
public $repository;
}
cokoliv jiného, musíš to zaregistrovat jako UploadFilesRepository do configu:
class CustomService {
public function __construct(App\Model\UploadFilesRepository $repository) { ... }
}
- cafesk8
- Člen | 103
Martk napsal(a):
namespace App\Model; use Nette\SmartObject; use Nette\Utils\FileSystem; use Zet\FileUpload\Model; use WebChemistry\Images\IStorage; class UploadFilesRepository implements \Zet\FileUpload\Model\IUploadModel { use SmartObject; public $storage; public $upload; public function __construct (IStorage $storage, Upload $upload) { $this->storage = $storage; $this->upload = $upload; } # další metody třídy }
config.neon
services: - App\Model\UploadFilesRepository
presenter:
class CustomPresenter { /** App\Model\UploadFilesRepository @inject */ public $repository; }
cokoliv jiného, musíš to zaregistrovat jako UploadFilesRepository do configu:
class CustomService { public function __construct(App\Model\UploadFilesRepository $repository) { ... } }
namespace App\Model;
use Nette\SmartObject;
use Nette\Utils\FileSystem;
use Zet\FileUpload\Model;
use WebChemistry\Images\IStorage;
class UploadFilesRepository implements \Zet\FileUpload\Model\IUploadModel {
use SmartObject;
public $storage;
public $upload;
public function __construct (WebChemistry\Images\IStorage $storage, Upload $upload) {
$this->storage = $storage;
$this->upload = $upload;
}
Mi teď pro změnu vyhazuje
Service '36_App_Model_UploadFilesRepository' (type of App\Model\UploadFilesRepository): Class App\Model\WebChemistry\Images\IStorage needed by App\Model\UploadFilesRepository::__construct() not found. Check type hint and 'use' statements.
Není někde nějaký sandbox kterým bych se mohl inspirovat?
Díky
- Ondřej Kubíček
- Člen | 494
tak ta hláška mluví jasně, máš špatně definovaný namespace
tohle:
... __construct (WebChemistry\Images\IStorage ...
hledá ve tvém namespacu App\Model\WebChemistry\Images\IStorage
protože nemáš v use use WebChemistry
nebo opačně, mít v constructoru jen IStorage
a
v use WebChemistry\Images\IStorage
ale to jsou základy PHP, nemá s nette ani s addonem nic společného, a hlavně už i idečko ti muselo řvát, že to nenašlo tu třídu
Editoval Ondřej Kubíček (21. 6. 2018 15:13)
- cafesk8
- Člen | 103
Zdravím,
mám problém s vykreslením obrázků. Přes ID obrázku si načtu adresu obrázku a do šablony si tuto cestu předám.
$resource = $this->imageStorage->createResource('moje_slozka/soubor.jpg');
$this->template->image = $this->imageStorage->link($resource);
v šabloně při zavolání klasického {$image} se mi vypíše ‚/assets/moje_slozka/original/soubor.jpg‘ Což je v pořádku.
Problém nastane, když bych si chtěl obrázek vygenerovat a vypsat ve více velikostech, zkusil jsem si dát do config.neon:
aliases:
resize: 'resize:$1,$2,$3'
resizeExact: 'resize:$1,$2,exact'
resizeSquare: 'resize:$1,$1'
crop: 'crop:$1,$2,$3,$4'
resizeCrop: 'resize:$1,$2,exact|crop:$3,$4,$5,$6'
Teď když zkusím v šabloně dát:
{img $image, resize(20,30,shrink)}
{img $image, resizeExact(20,30)}
{img $image, resizeSquare(10), crop(5,5,5,5)}
Tak mi vyskočí Undefined variable: _imageFacade viz. tato odpověď a dotaz nad ním . Dobře, tak jsem si do presenteru zaregistroval novou proměnnou a zároveň si ji předal do šablony
Super, hláška zmizela, ale teď {img $image} nezobrazí vůbec nic a {img $image resize(20,30,shrink}, apod. vyhazuje, že nejsou definované funkce jako je resize, resizeExact apod. Ty resizery apod se musí někde speciálně ještě napsat?
- flamengo
- Člen | 135
@Martk Ahoj, neměl by jsi někde nějaký ukázky, jak přejít z verze 2.1.4 na aktuální? V minulsoti jsme zkusil aktivovat novější verzi, ale nepodařilo se. Teď zkouším opět a…zase nic :(
V komponentě se snažím získat adresu obrázku, ve staré verzi to bylo takto:
$this->imageStorage->get($image, ¨100x100¨)->getLink()
S aktuální jsme začal takto:
$resource = $this->imageStorage->createResource($image);
A hned jsem skončil:
Error
Call to undefined method
WebChemistry\Images\Template\ImageFacade::createResource()
Do komponenty mám přes konstruktor vloženou závislost:
WebChemistry\Images\Template\ImageFacade
Co dělám špatně? Předem díky moc.
Editoval flamengo (10. 1. 2020 13:58)
- nightfish
- Člen | 517
flamengo napsal(a):
@Martk Ahoj, neměl by jsi někde nějaký ukázky, jak přejít z verze 2.1.4 na aktuální? V minulsoti jsme zkusil aktivovat > S aktuální jsme začal takto:
$resource = $this->imageStorage->createResource($image);
A hned jsem skončil:
Error
Call to undefined method WebChemistry\Images\Template\ImageFacade::createResource()
@flamengo ImageFacade
nemá metodu createResource()
. Avšak vypadá to, že má metodu
create()
, která interně createResource()
nad
storage volá.
Takže v prvním kroku bych vyzkoušel přepsat to na
$resource = $this->imageStorage->create($image);
a uvidíš, co se stane.
- mordare
- Začátečník | 117
@flamengo Snažíš se získat link v Latte nebo v PHP? ImageFacade se podle toho co vím používá aby fungovalo Latte makro.
Já si do komponent předávám přez konstruktor IImageStorage. A nad ním už můžeš volat to cos psal. Tj.:
public function __constructor(IImageStorage $imageStorage){
$this->imageStorage = $imageStorage;
}
public function doThing():void{
// get image
$resource = $this->imageStorage->createResource($image);
$link = $this->imageStorage->link($resource)
}
- jurajvt
- Člen | 18
Ahoj.
Ako riešite setDefaults()
pri formulároch?
V edit handle nastavujem formulárovému prvku typu
AdvancedUploadControl hodnotu typu
FileResource pomocou
$storage->createResource()
.
V tomto kroku setValue()
v AdvancedUploadControl nastaví pre
defaultValue práve vyššie spomenutú hodnotu.
Vo výstupe sa mi správne vykreslí náhľad obrázka a checkbox.
Po potvrdení formulára je však StateResource prázdne a
v spracovaní formulára kvôli tomu funkcia
$values->image->getDelete()
nevráti resource. Nemám čo
predať do $storage->delete()
.
Nevšimol som si, aby to tu niekto riešil, zrejme robím niekde chybu.
Napadá vás niečo?
- Martk
- Člen | 661
Proč v handle metodě?
Nette může volat jen jeden handle a formulář je taky handle, takže při odeslání formuláře se kód nevykoná.
Jsou jen 2 možnosti kde nastavovat správně setDefaults:
- V továrničce
- V metodě createComponent*
Kdekoliv jinde se to nevykoná při odeslání formuláře.
- jurajvt
- Člen | 18
Martk napsal(a):
Proč v handle metodě?
Nette může volat jen jeden handle a formulář je taky handle, takže při odeslání formuláře se kód nevykoná.
Jsou jen 2 možnosti kde nastavovat správně setDefaults:
- V továrničce
- V metodě createComponent*
Kdekoliv jinde se to nevykoná při odeslání formuláře.
Nerozumiem.
Vo factory triede pre formulár mám niečo také:
...
$form->addImagePreviewUpload('picture', 'Picture')
->setDelete('Remove')
->setPreviewAlias('preview')
->setNamespace('foo');
...
Vytváranie nového záznamu funguje bez problémov.
Idem editovať existujúci záznam – zavolám handle metódu, v ktorej naplním formulár hodnotami:
public function handleEdit(int $id)
{
...
$record = $this->repository->getById($id);
$this['myForm']->setDefaults(['picture' => $this->storage->createResource($record['picture'])])
...
}
Takto to robím vždy, keď idem editovať záznamy, nikdy som s tým
problém nemal.
Robím to nesprávne?
- Martk
- Člen | 661
Budeš muset udělat tohle:
- Továrnička:
class Factory {
...
/** @var FileResource|null */
private $picture; // + setter
...
public function create() {
...
$form->addImagePreviewUpload('picture', 'Picture')
->setDefaultValue($this->picture)
...
}
}
a v presenteru:
$this->factory->setPicture($this->storage->createResource($record['picture']));
před tím, než zavoláš $this['myForm']
nebo
$this->getComponent('myForm')
Editoval Martk (26. 2. 2020 10:20)
- flamengo
- Člen | 135
Ahoj @Martk , narazil jsem na problém u verze 3.0, neumí pracovat s EXIF vs. Orientation, takže některé fotky pořízené mobilem to nezpracuje správně. Nemáš v rukávu nějakou rychlou záplatu na tuto verzi? Koukal jsem, že ve verzi 4.1.8 to řešeno je. Klidně si upravím knihovnu ve vendoru, což jsme už zkoušel:
- do BaseModifiers jsem doplnil fixOrientation
- Upravil jsem alias v configu:
aliases:
adm-thumb: ‚resize:400,400,shrink_only,fixOrientation‘
Ale hlásí mi to: Flag ‚fixOrientation‘ not exists.
Předem díky za nakopnutí.
- kralik
- Člen | 230
Ahoj,
prosím nemáte někdo k dispozici příklady jak si vytvořit vlastní
Modifikátor?
Hledal jsem nějaké funční příklady vlastních Modifikátorů, ale nenašel
jsem nic.
Zkouším na Nette 3.0.x z dokumentace toto:
<?php declare(strict_types = 1);
use Nette\Utils\Image;
use WebChemistry\Images\ImageStorageException;
use WebChemistry\Images\Modifiers\Params\ModifierParam;
use WebChemistry\Images\Modifiers\Params\ResourceModifierParam;
class ImageModifier implements WebChemistry\Images\Modifiers\ILoader {
public function load(WebChemistry\Images\Modifiers\ModifierContainer $modifierContainer) {
$modifierContainer->addModifier('custom', function (ModifierParam $param, $foo) {
// zpracovani obrazku $param->getImage()
});
}
}
Dostávám chybu:
Declaration of ImageModifier::load(WebChemistry\Images\Modifiers\ModifierContainer $modifierContainer) must be compatible with WebChemistry\Images\Modifiers\ILoader::load(WebChemistry\Images\Modifiers\IModifiers $modifiers): void
Moc díky za radu.
- Ondřej Kubíček
- Člen | 494
@kralik a z té chyby jsi nepochopil co? implementuješ metodu
load
a ta musí mít stejný signature jako její
definice, tedy:
public function load(WebChemistry\Images\Modifiers\IModifiers $modifiers): void;
- kralik
- Člen | 230
Ondřej Kubíček napsal(a):
@kralik a z té chyby jsi nepochopil co? implementuješ metodu
load
a ta musí mít stejný signature jako její definice, tedy:public function load(WebChemistry\Images\Modifiers\IModifiers $modifiers): void;
Ano, pro oči nevidím.
Díky.
Prosím nevíš jestli jsou někde příklady vlastních Modifikátorů?
Např. vytvořit efekt blur
Díky
- Martk
- Člen | 661
@kralik Budeš muset hledat na google php gd blur, používá to totiž nette/image, které používá knihovnu gd.
Už delší dobu připravuji lepší verzi (https://github.com/…mage-storage + další balíčky), která umí použít jak gd, tak imagick nebo gmagick (u imagick stačí na blur použít $image->blurImage()). Dá se to použít standalone, na nette nebo symfony. Teď to používám v kombinaci nette/symfony + google cloud + gumlet + doctrine, tohle nešlo u tohoto balíčku snadno udělat.
Editoval Martk (17. 11. 2020 22:46)