Odeslání formuláře vč. vloženého souboru a následné průběžné odesílání
- Tirus91
- Člen | 199
Potřeboval bych ajaxem odeslat formulář a ten následně zpracovat.
Zpracování při neAjaxovém požadavku mi funguje, ovšem když se jedná
o ajax, tak mi to nepošle i přiložený soubor.
O co mi jde?
Mám formulář s následujícími prvky
1× select
1× input[type=text]
1× input[type=file]
Při vložení souboru se automaticky formulář odešle a soubor se uloží. Pomocí invalidace snippetu mu zaměním formulář tak, aby viděl náhled fotografie a input[type=text] mu nahradím s událostí onkeyup (také odesílání formuláře k uložení). Formulář je v komponentě a Xkrát zduplikovaný přes multiplier.
Komponenta
protected function createComponentAddPhoto() {
$form = new Form;
$form->getElementPrototype()->class[] = 'ajax form-horizontal';
if ($this->translator instanceof \Nette\Localization\ITranslator) {
$form->setTranslator($this->translator);
}
$form->addSelect('albums', 'Albums:', $this->albumRepository->findAll()->fetchPairs('id', 'title'))
->setAttribute('class', 'form-control');
$form->addText('title', 'Title:')
->setAttribute('class', 'form-control');
$form->addUpload('photo', 'Photography:')
->setAttribute('class', 'form-control');
$form->addSubmit('save', 'Save')
->setHtmlId('send-button')
->setAttribute('class', 'btn btn-primary');
$form->onSuccess[] = $this->completeSaveSettings;
return $form;
}
public function completeSaveSettings(Form $form) {
if ($form->isSuccess()) {
$values = $form->getValues();
if ($values->photo->isOk()) {
try {
$entity = new \Tirus\Entity\GalleryPhoto;
$entity->savePhoto($values->photo, $this->storage, $this->gallerySettings);
$entity->created = new \DateTime;
$entity->owner = $this->presenter->getUser()->id;
$entity->album = $values->albums;
$entity->title = $values->title;
$id = $this->photoRepository->persist($entity);
$entity = $this->photoRepository->get($id);
} catch (\Exception $ex) {
\Tracy\Debugger::log($ex);
$form->addError('Cant save photo');
return;
}
$this->flashMessage('Obrazek byl ulozen', 'success');
} else {
$this->flashMessage('Musite nejdriv vybrat obrazek', 'danger');
}
if (!$this->presenter->isAjax()) {
$this->redirect('this');
}
$this->redrawControl('addPhotoForm');
}
}
šablona
{snippet addPhotoForm}
<div class="col-md-6">
<div n:foreach="$flashes as $flash" class="row">
<div data-alert class="alert alert-{$flash->type}">{_$flash->message}</div>
</div>
{control addPhoto}
</div>
{/snippet}
pro ajax využívám nette.ajax.js
- wodCZ
- Člen | 49
nette.ajax.js neumí odesílat soubory, protože to neumí (neuměl) samotný
XHR.
Řešení třeba na http://stackoverflow.com/…load/4943774#… –
v komentářích se ale píše, že to využívá XHR2
Editoval Inkode (10. 9. 2014 20:03)
- wodCZ
- Člen | 49
Lze využít různé pluginy, které umí buď xhr2 nebo fallbacknou na flash, ale už to nebude krásně spolupracovat s Nette.
Přemýšlel jsem řešit to v JS filereaderem a posílat jako base64 string, ale nevím jestli to není úplná blbost. Pokud by to ale šlo, tak by to šlo implementovat docela jednoduše ručním voláním jako:
var frmData = {};
form.serializeArray().map(function (val, i) {
var pos = val.name.indexOf('[]');
if (pos !== -1) {
var key = val.name.slice(0, pos);
if (frmData[key] == undefined) {
frmData[key] = [val.value];
} else {
frmData[key][frmData[key].length] = val.value;
}
} else {
frmData[val.name] = val.value;
}
});
frmData['soubor'] = 'nejaky vystup z http://www.html5rocks.com/en/tutorials/file/dndfiles/'
$.nette.ajax({
url: 'xxx',
method:'post',
data: frmData
});
- na straně php zjistit, zda je to ajax a pokud ano, tak dekódovat, …
ALE možná existuje elegantnější řešení, počkej si, jestli odpoví někdo zkušenější
- Tirus91
- Člen | 199
@Inkode
super, toto je pro mne jednodušší :) .. Já jsem si zatím udělal dropdown a
něco s tím pokusím udělat a třeba mi to následně bude stačit. Problém
je ten, že budu moci odesílat jen formulář kde je daný soubor…
a i když je to base64, tak mi to nějak ani nevadí (jo jen problém že to asi nezvládne validaci u addUpload)
Asi to udělám tak, že když tam dragAndDropnu soubory, tak je budu postupně jeden po druhém odesílat a rovnou je odtamtud odebírat a invalidovat snippet se seznamem fotek. To by možná bylo celkem fajn.
Editoval Tirus91 (10. 9. 2014 23:25)
- Tirus91
- Člen | 199
@matej21
a odesílat to mám jak? Když to pošlu takto
var data = {
file: reader.result,
name: file.name
};
$.nette.ajax({
type: "POST",
dataType: 'json',
url: {link upload!},
data: JSON.stringify(data, null, 2)
});
tak mi tam příjde request->post a v tom je jakési pole které nemohu ani indexovat normálně
- David Matějka
- Moderator | 6445
zkus to uploadovat nejak takhle: http://stackoverflow.com/…-with-jquery
- Tirus91
- Člen | 199
@matej21
To mi asi nepůjde, jelikož nahrávání provádím pomocí
{form addPhoto}
{input files}
<div class="drop_zone_container">
<div id="drop_zone" style="">{_'Drop files here'}</div>
<output id="list"></output>
</div>
{block scripts}
<script>
function handleFileSelect(evt) {
evt.stopPropagation();
evt.preventDefault();
console.log(evt);
if (evt.target.id == 'inputFiles') {
var files = evt.target.files; // FileList object.
} else {
var files = evt.dataTransfer.files; // FileList object.
}
console.log(files);
// files is a FileList of File objects. List some properties.
var output = [];
var maxSize = {$maxUpload};
for (var i = 0, f; f = files[i]; i++) {
if (!f.type.match('image.*')) {
continue;
}
var bigger = false;
if (f.size > maxSize) {
bigger = true;
}
output.push('<li class="bg-info" id="file_' + i + '"><strong>', escape(f.name), '</strong> (', f.type || 'n/a', ') - ',
f.size, ' bytes',
'</li>');
if (bigger) {
continue;
}
(function(index, file) {
reader = new FileReader();
reader.onload = function(e) {
console.log(e);
document.getElementById('file_' + index).className = 'bg-success';
var postedData = {
file: reader.result,
name: file.name
};
$.nette.ajax({
type: "POST",
dataType: 'json',
enctype: 'multipart/form-data',
url: {link upload!},
data: postedData
});
//$('#file_' + index).fadeOut('slow');
};
reader.readAsText(f)
})(i, f);
}
document.getElementById('list').innerHTML = '<ul class="files">' + output.join('') + '</ul>';
}
function handleDragOver(evt) {
evt.stopPropagation();
evt.preventDefault();
evt.dataTransfer.dropEffect = 'copy'; // Explicitly show this is a copy.
}
// Setup the dnd listeners.
document.getElementById('inputFiles').addEventListener('change', handleFileSelect, false);
var dropZone = document.getElementById('drop_zone');
dropZone.addEventListener('dragover', handleDragOver, false);
dropZone.addEventListener('drop', handleFileSelect, false);
</script>
{/block}
{/form}
chtěl jsem to dát do base64 a odeslat jako string
Editoval Tirus91 (11. 9. 2014 20:01)
- Tirus91
- Člen | 199
@matej21
Tak jsem to uz rozjel :) .. díky za rady. Už to posílám jako data URL. Jen
mám jeden problém. Když tam vložím více souborů, tak se mi všechny
zruší a odešle se jen ten poslední. Čím to může být?
Aborted
Accept */*
Accept-Encoding gzip, deflate
Accept-Language cs,en-us;q=0.7,en;q=0.3
Content-Length 2638306
Content-Type application/x-www-form-urlencoded; charset=UTF-8
Cookie tirus_cms=0vsbm93801epnpaik3ksetq400; nette-browser=15h1yf5mqo
Host cms.local
Referer http://cms.local/Administration/Galleries/photo/
User-Agent Mozilla/5.0 (Windows NT 6.3; WOW64; rv:32.0) Gecko/20100101 Firefox/32.0
X-Requested-With XMLHttpRequest
- David Matějka
- Moderator | 6445
to asi dela unique
extension v nette.ajax.js
muzes ho vypnout, myslim, ze pomoci
$.nette.ext("unique", null);
- Tirus91
- Člen | 199
@matej21
díky, tím jsem narazil na problém se session..
Zkusil jsem dle https://forum.nette.org/…fy-chyba-303
doplnit session Id, ale asi špatně
Také když mi to někdy spadne na této exception, tak ajax to vyhodnotí, že bylo vše OK a provede mi to kod v success
$.nette.ext("unique", null);
$.nette.ajax({
type: 'POST',
url: {link upload!},
data: {
file: e.target.result,
fileType: fi.type,
fileName: fi.name,
fileSize: fi.size,
{=session_name()}: {=session_id()}
},
success: function(data, status, xhr) {
}
});
Editoval Tirus91 (11. 9. 2014 21:15)
- Tirus91
- Člen | 199
@Inkode
Nevím co přesněji myslíš.
Zde je celé vykreslení formuláře
{form addPhoto}
{input files}
<div class="drop_zone_container">
<div id="drop_zone" style="">{_'Drop files here'}</div>
<output id="list"></output>
</div>
{block scripts}
<script>
function handleFileSelect(evt) {
evt.stopPropagation();
evt.preventDefault();
console.log(evt);
if (evt.target.id == 'inputFiles') {
var files = evt.target.files; // FileList object.
} else {
var files = evt.dataTransfer.files; // FileList object.
}
console.log(files);
// files is a FileList of File objects. List some properties.
var output = [];
var maxSize = {$maxUpload};
for (var i = 0, f; f = files[i]; i++) {
if (!f.type.match('image.*')) {
continue;
}
var bigger = false;
if (f.size > maxSize) {
bigger = true;
}
var itemLi = '<li class="bg-';
if(bigger){
itemLi += 'danger';
}else{
itemLi += 'info';
}
itemLi += '" id="file_' + i + '"><strong>'+ escape(f.name)+ '</strong> ('+ f.type + ') - '+f.size+ ' bytes'+'</li>';
output.push(itemLi);
if (bigger) {
continue;
}
(function(index, fi) {
reader = new FileReader();
reader.onload = function(e) {
console.log(e);
$.nette.ext("unique", null);
$.nette.ajax({
type: 'POST',
url: {link upload!},
data: {
file: e.target.result,
fileType: fi.type,
fileName: fi.name,
fileSize: fi.size,
{!=session_name()}: {=session_id()}
},
success: function(data, status, xhr) {
document.getElementById('file_' + index).className = 'bg-success';
$('#file_' + index).fadeOut('slow');
},
error: function(){
document.getElementById('file_' + index).className = 'bg-danger';
}
});
};
reader.readAsDataURL(f)
})(i, f);
}
document.getElementById('list').innerHTML = '<ul class="files">' + output.join('') + '</ul>';
}
function handleDragOver(evt) {
evt.stopPropagation();
evt.preventDefault();
evt.dataTransfer.dropEffect = 'copy'; // Explicitly show this is a copy.
}
// Setup the dnd listeners.
document.getElementById('inputFiles').addEventListener('change', handleFileSelect, false);
var dropZone = document.getElementById('drop_zone');
dropZone.addEventListener('dragover', handleDragOver, false);
dropZone.addEventListener('drop', handleFileSelect, false);
</script>
{/block}
{/form}
Odesílá se následující
fileName P1010213 - kopie.JPG
fileSize 1904966
fileType image/jpeg
tirus_cms jur2gl3691psr14o6u1hu2eva1
file DATA URL obrázku
hlavička požadavku
Accept */*
Accept-Encoding gzip, deflate
Accept-Language cs,en-us;q=0.7,en;q=0.3
Content-Length 2744112
Content-Type application/x-www-form-urlencoded; charset=UTF-8
Cookie tirus_cms=jur2gl3691psr14o6u1hu2eva1; nette-browser=15h1yf5mqo
Host cms.local
Referer http://cms.local/Administration/Galleries/photo/
User-Agent Mozilla/5.0 (Windows NT 6.3; WOW64; rv:32.0) Gecko/20100101 Firefox/32.0
X-Requested-With XMLHttpRequest
A jako hlavička odpovědi je
Cache-Control no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Connection close
Content-Length 1082
Content-Type text/html
Date Thu, 11 Sep 2014 20:07:34 GMT
Expires Thu, 19 Nov 1981 08:52:00 GMT
Pragma no-cache
Server Apache/2.4.9 (Win64) OpenSSL/1.0.1h PHP/5.5.14
Set-Cookie tirus_cms=jur2gl3691psr14o6u1hu2eva1; expires=Fri, 11-Sep-2015 20:07:41 GMT; Max-Age=31536000; path=/; httponly nette-browser=15h1yf5mqo; path=/; httponly
X-Powered-By PHP/5.5.14
X-Tracy-Error-Log C:\wwwroot\htdocs\tirus_local\log\exception-2014-09-11-22-07-41-bf1a412b9e54228f3a271b2c8871d8ba.html
Editoval Tirus91 (11. 9. 2014 22:12)
- Tirus91
- Člen | 199
@Inkode
http://tirus.eu/exception.html
dal jsem celý soubor sem
co tak koukám, tak to nastává když provádím relogin (kvůli aktualizaci identity – je to tam kvůli tomu abych zajistil aktuální data i v případě že admin někomu změní roli nebo něco jiného)
Tak po zakomentování to funguje. Jak to mohu nahradit ten relogin? Potřebuji nahradit seznam rolí i identitu :(
Dále, jak mohu udělat postupné nahrávání těch souborů? (aby se to nenahrávalo najednou)
To samé, zda by šlo udělat abych ten soubor rovnou odesílal. Začalo mi to i padat na nedostatku paměti :(
Editoval Tirus91 (12. 9. 2014 0:59)