Multiple file upload
- Hellish
- Člen | 16
Chtěl bych se zeptat, jestli se neplánuje rozšířit třída Form o upload více souborů najednou. Klasické html tohle neumí, ale řešení existuje, viz např. Yahoo UI Uploader – http://developer.yahoo.com/yui/uploader/
Jistě mohu tuto Yahoo komponentu použít i v Nette frameworku a nějak ji napojit na Nette Form, ale kdyby tuto možnost měl Nette Form již v základu implementovanou, tak by to bylo super.
Co myslíte?
- yogiman321
- Člen | 11
Ja sa tiez snazim rozchodit ten uploadify, ale je to zatial marna snaha… ocenil by som ak by niekto pridal nejaky funkcny priklad nastavenia uploadify aj s uvedenim verzie a verzie jquery.
edit:
skusal niekto toto?: http://www.jcupload.com
Editoval yogiman321 (9. 7. 2009 12:47)
- David Grudl
- Nette Core | 8218
Takova komponenta by byla bozi, nejaký upload ala Flickr. Vznik bych klidně finančně nebo recipročně podpořil.
- muta
- Člen | 21
Mám rozchozené nahrávání více (neomezeně) souboru najednou na jednom projektu. Umí to vybrat určité typy souborů, při nahrávání to ukazuje progress bar s aktuálním stavem nahrávání u každého souboru, nahrává to větší soubory (zkoušel jsem řádově několik MB na soubor) … Bohužel to není v nette. Pokud byl zájem, přepíši ji do nette jako komponentu. Jen sem pište co všechno by bylo dobré nastavovat a nejspíše budu hodně otravovat s dotazama, protože s nette se ještě pořád seznamuji (půl roku :).
- Hellish
- Člen | 16
No Flickr právě využívá ten Yahoo UI Uploader: http://developer.yahoo.com/yui/uploader/
Ještě jsem to nezkoumal podrobně, ale myslím, že použít ten Yahoo uploader je jednoduché. Otázkou zůstává to provázání s Nette Form.
- JakubJarabica
- Gold Partner | 184
Mne sa veľmi pozdáva aj tento uploader: http://digitarald.de/…fancyupload/, konkrétne táto showcase s multiuploadom: http://digitarald.de/…/photoqueue/. Uploader je vo svojej tretej verzii(poslednú som neskúšal, staršie hej a bol to nárez).
Napísaný je pre Mootools >= 1.2, je to jedna veľká modifikovateľná trieda(pozrite si options a features) a autor dbá aj na sémantiku a podobné veci, ponúkam to len ako alternatívu s ktorou sa mi zatiaľ najlepšie a hlavne dosť rýchlo pracovalo.
Ak sa do Nette dostanem trošku viac(biedny začiatočník, zatiaľ), môžem pomôcť s implementáciou, prípadne keby niekto potrebuje nejaký JS help s Mootools pri implementácii tohoto, rád pomôžem.
Editoval JAM3SoN (9. 7. 2009 19:51)
- Panda
- Člen | 569
Takže po fragmentovaném (tzn. základ včera, promyšlení přes noc, dokončení dnes) snažení jsem vytvořil formulářovou komponentu Uploadify pro Nette (jQuery). Bohužel jsem nenašel žádný rozumný způsob, jak přenést session i na flashový uploader, tak jsem použil tokeny a upload se musí zpracovávat mírnou oklikou. Také nutno podotknout, že komponenta stále ještě není hotová. Už teď bych ale byl rád, kdyby jí někdo zkusil otestovat a přidal případné nápady.
Soubor s komponentou ke stažení: http://jan.smitka.org/…leUpload.zip
// Aktualizace #1: http://jan.smitka.org/…eUpload2.zip
Co chybí a co je potřeba ještě dokončit:
- validace
- zpracování chyb + podpora pro chybová hlášení ze serveru do Uploadify (souvisí s validací)
- poladit JavaScript
- úprava kódu (refactoring, rozdělení do více souborů, …)
- otestování kompatibility (testoval jsem jen ve FF 3.5)
postupné prodlužování platnosti tokenu během uploadu jednotlivých souborůhotovo- atomické operace bez
SafeStream
Použití
Do stránky je potřeba vložit přiložené skripty a CSS soubory.
Jak jsem naznačil na začátku, je potřeba upload zpracovávat oklikou.
Před ověřením uživatele je potřeba zavolat metodu
MultiFileUpload::handleUploads
. Pokud máme ověření uživatele a
případný redirect na přihlašovací stránku, bude to v presenteru vypadat
nějak takto:
<?php
public function startup()
{
$this->getUploader()->handleUploads($this);
parent::startup();
}
private $uploader;
/**
* @return MultiFileUpload
*/
public function getUploader()
{
if ($this->uploader === NULL) {
$this->uploader = new MultiFileUpload('Soubory:', array('buttonText' => 'Procházet'));
}
return $this->uploader;
}
?>
Tento způsob zpracování je žhavý kandidát na změnu.
Ve formuláři se pak pole použije nějak takto:
<?php
$form['files'] = $this->getUploader();
?>
Konstruktor třídy MultiFileUpload
má 2 parametry:
$caption
(jako jiné formulářové prvky) a
$options
– ten slouží pro nastavení Uploadify. Jejich seznam
naleznete zde: http://www.uploadify.com/documentation/. Pak jdou také tyto
volby nastavovat po vytvoření pomocí metody
setUploaderOption($key, $value)
.
Přiložený soubor multifileupload.js
najde všechny
uploadovací prvky a jejich formulářům nastaví vlastní odesílací handler:
nejprve se nahrají soubory a pak se automaticky odešle formulář. Zatím
není ještě pořádně odladěný, takže když nevyberete žádný soubor,
formulář nelze odeslat.
Při vytvoření komponenty se vygeneruje token. Jednotlivé tokeny jsou
uloženy v souboru %tempDir%/multifileupload/.tokens
. Staré
tokeny se postupně odmazávají. Jejich expiraci lze nastavit pomocí
MultiFileUpload::getTempStorage()->expires = 3600;
// Pozor: instance implementující IMultiFileUploadTempStorage
// je uložena ve statické vlastnosti MultiFileUpload::$storage!
// Je tedy pro všechny instance společná.
Výchozí hodnota je zatím pro účely testování nastavena na 600, tedy
10 minut. Při uploadu souboru se ve složce se souborem .tokens
vytvoří podsložka, jejíž jméno bude stejné jak kód tokenu. Do ní se
umístí nahraný soubor pod dočasným jménem a vytvoří se soubor
.list
, který obsahuje informace o nahraných souborech pro daný
token. Tyto složky se také postupně odmazávají (stejná doba expirace jako
token, zde se však nepočítá podle doby vytvoření, ale podle poslední
modifikace). Se souborem .tokens
a soubory .list
se
pracuje atomicky pomocí SafeStream
. Této „závislosti“ bych
se později rád zbavil.
Díky rozhraní IMultiFileUploadTempStorage
je možné
s nahranými soubory pracovat úplně jinak.
Pokud má někdo lepší nápad, jak to provést, sem s ním.
Po odeslání formuláře máme mezi jeho hodnotami přístupné pole
objektů MultiUploadFile
. Jejich rozhraní je téměř shodné
s tím, jaké má HttpUploadedFile
, takže by měly být oba
objekty zaměnitelné.
Až bude komponenta dostatečně funkční a použitelná (ovšem pokud tato situace vůbec někdy nastane), půjde do extras. Tak a teď mě kamenujte. :-)
Editoval Panda (30. 7. 2009 23:05)
- Ondřej Mirtes
- Člen | 1536
Mám dotazy, jakožto začátečníka do Uploadify :) V PHP je hodně omezující, že obvykle bývá nastavenou na nějakou malou hodnotu limit velikosti uploadovaného souboru. Řeší tento problém Uploadify? Řeší nějak hrozbu přerušení uploadu? A jak funguje s vypnutým Javascriptem a Flashem? (objeví se normální pole pro nahrání souboru?)
- Panda
- Člen | 569
LastHunter napsal(a):
Mám dotazy, jakožto začátečníka do Uploadify :) V PHP je hodně omezující, že obvykle bývá nastavenou na nějakou malou hodnotu limit velikosti uploadovaného souboru. Řeší tento problém Uploadify? Řeší nějak hrozbu přerušení uploadu? A jak funguje s vypnutým Javascriptem a Flashem? (objeví se normální pole pro nahrání souboru?)
- Velikost neřeší, stále je to klasický upload souboru pro PHP, tzn. je potřeba nastavit příslušnou direktivu v PHP
- Přerušení uploadu jsem netestoval, u error handlerů je však chyba typu „IO“, takže to snad nějak ošetřené bude
- V komponentě se objeví normální pole pro nahrání souboru a pole s výsledky bude mít jen jeden prvek
- PetrP
- Člen | 587
Panda napsal(a):
Výchozí hodnota je zatím pro účely testování nastavena na 600, tedy 10 minut. Při uploadu souboru se ve složce se souborem
.tokens
vytvoří podsložka, jejíž jméno bude stejné jak kód tokenu. Do ní se umístí nahraný soubor pod dočasným jménem a vytvoří se soubor.list
, který obsahuje informace o nahraných souborech pro daný token. Tyto složky se také postupně odmazávají (stejná doba expirace jako token, zde se však nepočítá podle doby vytvoření, ale podle poslední modifikace). Se souborem.tokens
a soubory.list
se pracuje atomicky pomocíSafeStream
. Této „závislosti“ bych se později rád zbavil.
Pozor zapisování do tebou vytvořených složek ti nemusí fungovat při save modu.
- Honza Kuchař
- Člen | 1662
Ještě jenom dodám. S flashem jsem měl prořád problémy. Cookies + https a tak podobně. Dost zajímavě vypadá nádledující kód (nemám ho momnetálně kam dát).
<?php
/**
* PHP Gears Multiple File Loader
* This application uses the Google Gears API, to select and load multiple files
*
* Resources:
* Brad Neuberg (http://codinginparadise.org)
* Gears (http://gears.google.com)
*
* @author Andrew Dodson
*/
if ( !empty( $_GET['n'] ) )
{
$fd = fopen("php://input", "r");
while( $data = fread( $fd, 10000000 ) ) file_put_contents( "./{$_GET['n']}", $data, FILE_APPEND );
}
?>
<html>
<head>
<title>Hello World for the File System API</title>
<script type="text/javascript" src="http://code.google.com/apis/gears/gears_init.js" ></script>
<script type="text/javascript" src="http://jqueryjs.googlecode.com/files/jquery-1.2.6.min.js" ></script>
<script type="text/javascript">
window.onload = function() {
if (!window.google || !google.gears) {
addStatus('Gears is not installed', 'error');
return;
}
}
/**
* Display information to the client
*/
function addStatus(s){ $("#status").append( s + "<br />" ); return 1;}
/**
* Get the minimum of two results.
*/
function min(a,b){ return (a<b?a:b); }
/**
* Open file browser window
*/
gearsupload = {
mylist: [],
fileName: "",
CHUNK_BYTES: 200000, // Send file in packets of 200KB
MAX_FILE_SIZE: 10000000,// Limit the total upload size to 10MB
UPLOAD_RETRIES: 3, // Number of retries
browse: function (){
var desktop = google.gears.factory.create('beta.desktop');
desktop.openFiles(
function(callback, that){
return function(){
return callback.apply(that, arguments);
}
}
(function(files) {
for ( var i = 0; i < files.length; i++ )
{
if ( this.mylist[files[i].name] ){ continue; } // Has the file by the same name already been selected?
this.mylist[files[i].name] = {
filename: files[i].name,
uploaded: 0,
length: files[i].blob.length,
blob: files[i].blob,
bytesUploaded: 0,
status: (files[i].blob.length>this.MAX_FILE_SIZE?"File too large":"Pending")};
addStatus( "Selected: " + files[i].name + " " + files[i].blob.length );
}
$('#upload').html('<a href="#upload" onclick="return gearsupload.upload();">Upload</a>');
},
this)
,
{ }
// { singleFile: true }
);
},
upload: function()
{
var chunkLength, chunk, mylist = this.mylist;
/**
* Loop through the files and upload the next file/chunk
*/
for ( file in mylist ) if ( ( mylist[file].uploaded < mylist[file].length && !mylist[file].error ) )
{
/**
* what is the current filename
*/
fileName = file;
chunkLength = min( mylist[file].uploaded + this.CHUNK_BYTES, mylist[file].length);
addStatus( "Uploading " + fileName + ": from " + mylist[file].uploaded + " to " + chunkLength );
/**
* Get the next chunk to send.
*/
chunk = mylist[file].blob.slice( mylist[file].uploaded, (chunkLength - mylist[file].uploaded) );
addStatus( "Chunk length " + chunk.length );
/**
* Send Chunk
*/
this.sendChunk( mylist[file], chunk, mylist[file].uploaded, chunkLength, mylist[file].length );
break;
}
},
sendChunk: function ( entry, chunk, start, end, total )
{
var req = google.gears.factory.create('beta.httprequest');
var prcnt = Math.ceil( ( end/total ) * 100 );
/**
* Start Post
*/
req.open('POST', '?n='+encodeURIComponent(fileName)+'&b='+encodeURIComponent(start) );
/**
* Assign Headers
*/
var h = { 'Content-Disposition' : 'attachment; filename="' + fileName + '"',
'Content-Type' : 'application/octet-stream',
'Content-Range' : 'bytes ' + start + '-' + end + '/' + total };
for( var x in h ) if (h.hasOwnProperty(x)) { req.setRequestHeader( x, h[x] ); }
/**
* Build Response function
*/
req.onreadystatechange = function(callback, that){
return function(){
return callback.apply(that, arguments);
}
}
(function(){
if (req.readyState == 4 && addStatus( "Resp: (" + req.status + ")" ) && req.status == 200 ) {
entry.uploaded = end;
addStatus( fileName + ( (end + 1) >= total ? " Finished" : ' Upload: so far ' + prcnt + '%' ) );
this.upload();
}
},this);
/**
* Send Chunk
*/
req.send(chunk);
}
};
</script>
</head>
<body style='font-family:verdana;'>
<div>
<a href="#select" onClick="return gearsupload.browse();">Select File</a>
<span id="upload"></span>
</div>
<p id="status"></p>
</body>
</html>
Používá to Google Gears
- Honza Kuchař
- Člen | 1662
Ještě dodám, Google Gears si můžete nainstalovat z http://gears.google.com/
- Vyki
- Člen | 388
Ahoj, mám s touto komponentou problém. Na localhostu mi vše funguje. Stáhnul jsem si přes SVN přesné zdrojové soubory ukázky uploadu ( https://svn.mujserver.net:8443/…pload/trunk/ ), ale na ostrých serverech píše uploadify ajax formulář u každého souboru po odeslání „HTTP Error“. Práva pro zápis mám na adresářích temp, session, uploadData povolena, ale stejně nefunguje. Děkuju za radu
- Vyki
- Člen | 388
Mrkni na phpinfo jednoho z tech serveru zde: http://home.zcu.cz/…ka/info.html
Hostované to je u active24.
Editoval Vyki (15. 11. 2009 11:22)
- Panda
- Člen | 569
Zkus MultipleFileUpload.php
upravit podle tohoto: https://forum.nette.org/…iewtopic.php?….
- Honza Kuchař
- Člen | 1662
@Panda: Díky opraveno.
Problémy s IE:
- flash ve <form>u je problém. IE si to přeloží jako
window.id.fce(..);
. Ale muselo by to býtwindow.form[x].id.fce(...)
. Řešení: pužívat patchlýswfobject.js
(je v distribuci) Ten objekt flashe „zkopíruje“ do objektuwindow
. (do toho co si vygenruje nejde zasahovat :( ) - flash s id, které obsahuje „-“ je taky problém. IE totiž potom
používá někde interně
id.nejakafce(...)
. Tzn. pak se to přeloží na něco typufrmform-test-text.fce(...)
. Tzn. odečítá od objektufrmform
test
a následnětext
. Ani jedno není definováno. → chyba → absolutně nepoužitelné. Řešení: používat patchnutýjquery.uploadify.v2.1.0.js
, který „-“ nahrazuje. - Je to hrozně pomalé. Opravdu se vyplatí používat jen u velkých souborů. Mezi soubory čeká. Při výběru čeká. Řešení: používat jakýkoli jiný moderní prohlížeč.
- iguana007
- Člen | 970
Dnes jsem objevil zajimavý multi-uploader, kdyby někoho zajímal: http://www.plupload.com/
Líbí se mi na něm kombinace: flash, silverlight, html5, google gears a yahoo plus.
Jak budu více zaběhlý v Nette možná bych jej zkusil udělat jako další verzi addonu …
- Vyki
- Člen | 388
iguana007 napsal(a):
Dnes jsem objevil zajimavý multi-uploader, kdyby někoho zajímal: http://www.plupload.com/
Vypadá to velice zajímavě a píšou tam, že to umí i resizovat obrázky :o)
- Honza Kuchař
- Člen | 1662
Vyki napsal(a):
iguana007 napsal(a):
Dnes jsem objevil zajimavý multi-uploader, kdyby někoho zajímal: http://www.plupload.com/
Vypadá to velice zajímavě a píšou tam, že to umí i resizovat obrázky :o)
Tak na to snad máme Nette/Image
.
- norbe
- Backer | 405
Občas je ale výhodnější obrázky resizenout už u klienta a uploadovat už zmenšený. Velikost fotografií může být i několik desítek MB a pokud jich mám např. 100, už je to problém (vysvětlovat některým uživatelům, že mají před uploadem použít ten a ten software na resize fotek s nějakým nastavením může být nadlidský úkon…).