Upload souboru na server a uložení cesty do db
- .:M@rt!n:.
- Člen | 201
Hoj, snažím se o upload souboru na server a následné uložení cesty
k souboru a popisu souboru do db.
Zatím mám toto: formulář pro upload
protected function createComponentAkceForm() {
$form = new Form();
$folder = $this->getParam('folder');
$form->addUpload('img', 'Soubor:')
//->addRule(Form::MIME_TYPE, 'Přílohou musí být obrázek formátu .jpg .', 'image/jpeg') ;
->addRule(Form::IMAGE, 'Soubor musí být JPEG, PNG nebo GIF.');
// ->addRule(Form::MAX_FILE_SIZE, 'Maximální velikost souboru je 5 MB.', '50000');
$form->addDatePicker('datum', 'Datum: ', 5);
$form->addText('popis', 'Popis: ', 50)
->addRule(Form::FILLED, 'Je nutné zadat informace o akci.');
$form->addHidden('folder', $folder);
$form->addSubmit('create', 'Nahrát');
$form->addSubmit('cancel', 'Zrušit')
->setValidationScope(FALSE);
$form->onSuccess[] = callback($this, 'akceFormSubmitted');
return $form;
}
a následná obsluha
public function akceFormSubmitted(Form $form) {
if ($form->isSubmitted()) {
if ($form['cancel']->isSubmittedBy()) {
// process cancelled
$this->flashMessage('Nahrávání zrušeno.', 'notice');
$this->redirect('this');
}
if ($form['create']->isSubmittedBy() && $form->isValid()) {
// submitted and valid
$values = $form->getValues();
/*
* Kontrola, zda-li byl obrazek skutecne nahran
*/
if ($values['img']->isOk()) {
} else {
$this->flashMessage('Obrázek se nezdařilo nahrát na server.', 'warning');
}
dump($values['img']->isOk());
}
}
}
Obsluha se mě zastaví o dump, což je v pořádku. Teď ale už moc nevím jak zajistit, aby se mě soubor nahrál do požadované složky, která bude umístěna ve „www/images/slozka“ a cesta k němu se uložila do db. Ze které budu potom budu tahat cestu pro zobrazení souboru (bude to pouze obrázek). Chtěl bych tímto tedy požádat o jakékoli rady, jak na to? předem díky
- duke
- Člen | 650
$form->values['img']
bude obsahovat instanci objektu
Nette\Http\FileUpload
(viz api).
Nejvíc tě asi budou zajímat metody isOk()
a move($dest)
.
Btw pokud se nemýlím, tak řádek:
if ($form->isSubmitted()) {
… je nadbytečný, neboť to již řeší „onSuccess
“.
Editoval duke (31. 3. 2012 17:29)
- duke
- Člen | 650
Volání isOk
už ve svém kódu máš, takže nějak takto:
if ($values['img']->isOk()) {
$filename = $values['img']->getSanitizedName();
$targetPath = $this->getImagesDir();
if ($values['folder'] !== '') {
$targetPath .= "/$values[folder]";
}
// @TODO vyřešit kolize
$values['img']->move("$targetPath/$filename");
// uložení do db, např.:
$this->db->table('images')->insert(array(
'file' => "$targetPath/$filename",
'description' => $values['popis'],
));
} else {
Příklad nicméně neřeší možné kolize souborů, ale to už je jiný problém, na který ses neptal.
EDIT: Ještě by tam měla být kontrola položky
$values['folder']
zda obsahuje povolené hodnoty, aby ti tam
náhodou někdo nepodstrkával např. '../../../neco'
nebo jména
neexistujících složek.
Editoval duke (31. 3. 2012 21:43)
- .:M@rt!n:.
- Člen | 201
Tak už jsem upload i uložení do db vyřešil.
Kód zpracování formuláře:
public function akceFormSubmitted(Form $form) {
if ($form['save']->isSubmittedBy()) {
$id = (int) $this->getParameter('id');
$file = $form['obrazek']->getValue();
$imgUrl = $this->context->params['wwwDir'] . '/images/upload/' . $file->name;
$file->move($imgUrl);
if ($id > 0) {
$this->context->createAkce()->find($id)->update(array(
'datumOd' => $form->values->datumOd,
'datumDo' => $form->values->datumDo,
'popis' => $form->values->text,
'url' => '/images/upload/' . $file->name
));
$this->flashMessage('Akce upravena.', 'success');
} else {
$this->context->createAkce()->insert(array(
'datumOd' => $form->values->datumOd,
'datumDo' => $form->values->datumDo,
'popis' => $form->values->text,
'url' => '/images/upload/' . $file->name
));
$this->flashMessage('Upload zrušen.', 'success');
}
//$this->flashMessage($dest);
$this->redirect('default');
}
}
Uploadnutý obrázek mám přiřazený k akci, ale teď bych zase
potřeboval, aby se mě ten obrázek ze složky, po smazaní akce, smazal
taky.
Kód zpracovaní formuláře pro smazání z db:
public function deleteFormSubmitted(Form $form) {
if ($form['delete']->isSubmittedBy()) {
$this->akce->find($this->getParameter('id'))->delete();
$this->flashMessage('Položka smazána.');
}
$this->redirect('default');
}
snažil jsem se zase získat adresu uloženého obrázku a podle ní pak obrázek smazat, ale zatím neúspěšně. Jak by se dalo vyřešit?
- duke
- Člen | 650
Záznam z databáze si načteš pomocí metody fetch.
$akce = $this->context->createAkce()->find($id)->fetch();
Pak můžeš smazat soubor přes:
if ($akce) {
unlink($this->context->params['wwwDir'] . '/images/upload/' . $akce->url);
}
Nicméně ještě několik výhrad:
- Nevím proč jsi odstranil volání isOk. To by tam určitě mělo zůstat.
- Opakuji, že
$form['save']->isSubmittedBy()
je nejspíš nadbytečné, pokud tedy nemáš víc submitovacích buttonů, které řešíš všechny metodou akceFormSubmitted. - Neřešíš kolize v názvech souborů. (mohlo by stačit místo
$file->name
použít$id . '_' .$file->name
) - Flash message „Upload zrušen.“ lže, má tam být „Akce vytvořena.“.
Editoval duke (3. 4. 2012 20:29)
- .:M@rt!n:.
- Člen | 201
Takže metoda pro smazání by mohla vypadat nějak takto?
public function deleteFormSubmitted(Form $form) {
$this->akce->find($this->getParameter('id'))->delete();
$akce = $this->context->createAkce()->find($id)->fetch();
if ($akce) {
unlink($this->context->params['wwwDir'] . '/images/upload/' . $id . '_' . $file->name);
}
$this->flashMessage('Položka smazána.');
$this->redirect('default');
}
- duke
- Člen | 650
Ne, to tedy nemohla.
- nemáš nastavenou proměnnou $id a $file
- proměnnou $akce se snažíš naplnit z databáze, až když jsou data v databázi smazána
Takže např. nějak takto:
public function deleteFormSubmitted(Form $form) {
$id = $this->getParameter('id');
// případně mít id v hidden inputu formuláře:
// $id = $form['id']->value;
if ($id) {
$akce = $this->context->createAkce()->find($id)->fetch();
if ($akce) {
unlink($this->context->params['wwwDir'] . '/images/upload/' . $id . '_' . $akce->url);
$akce->delete();
$this->flashMessage('Položka smazána.');
} else {
$this->flashMessage('Tato akce již neexistuje.', 'error');
}
} else {
throw new Nette\Application\BadRequestException('No id given for delete.');
}
$this->redirect('default');
}
A samozřejmě musíš to řešení kolizí (přes prefix $id_) přidat i do uploadovacího kódu.
- Senik
- Člen | 4
Mám dotaz ke zde řešenému, odpověď jsem hledal všude možně, ale nenašel. Do databáze přidávám inzeráty a při přidávání uploaduju obrázek, ten ale potřebuju ve složce odlišit podle IDčka toho inzerátu (to má v DB nastavený Auto Increment), dá se nějakým způsobem zjistit ID právě přidávaného záznamu do DB? Při úpravě inzerátu to je v pohodě, protože to ID už znám, ale nevím si rady, jak to zjistit při vytváření. Je vůbec něco takovýho možný?
Editoval Senik (7. 4. 2012 18:16)
- Filip Procházka
- Moderator | 4668
Je problém, nejprve uložit inzerát, zeptat se na jeho ID, pojmenovat soubor a pak upravit záznam v databázi? Jako bonus můžeš vynechat poslední krok, protože stejně víš, jak se souboru bude jmenovat.
Editoval HosipLan (7. 4. 2012 18:50)
- duke
- Člen | 650
Možná nevíš, že metoda insert
pro vkládání záznamů do
databáze vrací instanci ActionRow
představující právě
vložený řádek (obsahuje všechny původně vkládané hodnoty + primární
klíč). Takže můžeš udělat (v duchu toho, co ti už napsal HospiLan)
následující:
$inserted = $this->db->table('images')->insert(array(
'description' => $values['popis'],
));
$filename = $inserted['id'] . '_' . $filename;
// nyní buď:
$inserted['file'] = "$targetPath/$filename";
$inserted->update();
// nebo by mělo jít také:
// $inserted->update(array('file' => "$targetPath/$filename"));
Nebo se prostě smířit s tím, že v databázi uložíš název souboru bez prefixu a id k němu budeš přidávat až dodatečně (pak nepotřebuješ ten následný update, jak správně poznamenal HospiLan). Pokud zvolíš cestu toho následného update, měl bys operace insert a update provést v transakci, aby jiný skript nenačítal napůl uložený záznam.
- .:M@rt!n:.
- Člen | 201
Trochu jsem musel upravit upload souboru a teď se mě nevypisuje hláška, když chci na uploadovat jiný soubor než obrázek. Obsluha formu:
public function akceFormSubmitted(Form $form) {
$id = (int) $this->getParameter('id');
if ($id > 0) {
$this->context->createAkce()->find($id)->update(array(
'datumOd' => $form->values->datumOd,
'datumDo' => $form->values->datumDo,
'popis' => $form->values->popis
));
if ($form->values->agree) {
$file = $form['obrazek']->getValue();
$imgUrl = $this->context->params['wwwDir'] . '/images/upload/' . $id . '_' . $file->name;
$file->move($imgUrl);
$this->context->createAkce()->find($id)->update(array(
'url' => '/images/upload/' . $id . '_' . $file->name
));
}
$this->flashMessage('Akce upravena.', 'success');
} else {
$inserted = $this->context->createAkce()->insert(array(
'datumOd' => $form->values->datumOd,
'datumDo' => $form->values->datumDo,
'popis' => $form->values->popis
));
$newid = $inserted->id;
if ($form->values->agree) {
$file = $form['obrazek']->getValue();
$imgUrl = $this->context->params['wwwDir'] . '/images/upload/' . $newid . '_' . $file->name;
$file->move($imgUrl);
$this->context->createAkce()->find($newid)->update(array(
'url' => '/images/upload/' . $newid . '_' . $file->name
));
}
$this->flashMessage('Akce přidána.', 'success');
}
}
U vytvoření formuláře mám samozřejmě nastaveno
addRule(Form::IMAGE, 'Soubor musí být obrázek - .jpg, .gif, .png')
Už nevím, kde by mohl být problém?
- jtousek
- Člen | 951
V tom obslužném kódu akceFormSubmitted chyba nebude. Ta metoda se nemá vůbec co spouštět pokud neprošla validace formuláře. Co přesně ti to dělá? Validace projde, přestože by neměla nebo validace neprojde, ale hláška se nezobrazí?
EDIT: Mimochodem doporučuji nemíchat češtinu s angličtinou a zavést konvenci. ;-)
Editoval jtousek (7. 5. 2012 20:53)
- .:M@rt!n:.
- Člen | 201
Ano, validace projde (respektive neprojde,jde o to jak se na to díváš),
ale nezobrazují se hlášky. Čili chci nahrát něco jiného než obrázek a
při potvrzení mě to do db nic neuloží a odkáže mě to na vybrání
jiného souboru, ale nevyskočí hláška, že to musí být jpg.
Celý formulář je takto:
protected function createComponentAkceForm() {
$form = new Form();
$presenter = $this;
$form->addCheckbox('agree', ' Přidat nebo změnit obrázek')
->addCondition($form::EQUAL, TRUE)
->toggle('newPic');
$form->addGroup()
->setOption('container', \Nette\Utils\Html::el('td')->id('newPic'));
$form->addUpload('obrazek', 'Obrázek:')
->addConditionOn($form['agree'], $form::EQUAL, TRUE)
->addRule(Form::IMAGE, 'Soubor musí být obrázek - .jpg, .gif, .png')
->addRule(Form::MAX_FILE_SIZE, 'Soubor je příliš velký! Povolená velikost je 2M.', 2 * 1024 * 1024);
$form->addDatePicker('datumOd', 'Datum od: ', 5);
$form->addDatePicker('datumDo', 'Datum do: ', 5);
$form->addTextArea('popis', 'Popis akce:')
->getControlPrototype()->class('mceEditor');
$form->addSubmit('save', 'Uložit')->setAttribute('class', 'default');
$form->addSubmit('cancel', 'Zrušit')
->setValidationScope(FALSE)
->onClick[] = function () use ($presenter) {
$presenter->redirect('default');
};
$form->onSuccess[] = callback($this, 'akceFormSubmitted');
$form->addProtection('Vypršel časový limit, odešlete formulář znovu.');
return $form;
}
Mám to udělaný tak, že po zaškrtnutí toho checkboxu se mě zobrazí pole pro upload.
Editoval .:M@rt!n:. (7. 5. 2012 21:16)
- .:M@rt!n:.
- Člen | 201
Nn, to ne. Když tam dám například pdf a dám odeslat, tak se mě vybraný soubor z formuláře smaže a musim vybrat jiný. Ale nevyhodí mě to hlášku, že ten prvni vybraný není formát obrázku.
- .:M@rt!n:.
- Člen | 201
přes {form}:
{form akceForm}
<table>
<tr>
<td colspan="2">{input agree} Přidat nebo změnit obrázek</td>
</tr>
<tr id="newPic">
<td>{label obrazek/}</td>
<td> {input obrazek}</td>
</tr>
<tr>
<td>{label datumOd/}</td>
<td>{input datumOd}</td>
</tr>
<tr>
<td>{label datumDo/}</td>
<td>{input datumDo}</td>
</tr>
<tr>
<td>{label popis/}</td>
<td> {input popis}</td>
</tr>
<tr>
<td>{input save} {input cancel}</td>
</tr>
</table>
{/form akceForm}
- vvoody
- Člen | 910
Dobre som typoval :) ten formular sa ti validuje spravne, len ty cakas
javascriptovu hlasku, ale mime type sa asi kontroluje len na serverovej strane.
Problem je v tom ze ty nevypisujes errory zachytene na strane serveru.
https://doc.nette.org/cs/forms#…
<ul class="errors" n:if="$form->hasErrors()">
<li n:foreach="$form->errors as $error">{$error}</li>
</ul>
- .:M@rt!n:.
- Člen | 201
Jo díky,to jsem nějak přehlédl. Ale teď mě to zvaliduje pouze, jestli je to obrázek. Ale když nahraji obrázek větší než 2MB, tak to nevypíše hlášku: „Soubor je příliš velký! Povolená velikost je 2MB.“ ale tu první: „Soubor musí být obrázek – .jpg, .gif, .png“ která je definovaná u kontroly, zda je to obrázek…