Multiple file upload

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
Hellish
Člen | 16
+
0
-

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?

Jod
Člen | 701
+
0
-

Nedávno som to riešil v práci, ajaxový multiupload (cez frame), ma skoro porantalo z toho =)))) a dvakrát by ma keby som mal nato použiť nette/forms. Najjednoduchšie to máš ručne, alebo si napíš nejaký control, alebo by som mohol prepísať ten čo som robil, keby bol čas.

cuga
Člen | 210
+
0
-

tohle bych fakt ocenil, porad se marne snazim valcit s uploadify, ale prd z toho :)

yogiman321
Člen | 11
+
0
-

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
+
0
-

Takova komponenta by byla bozi, nejaký upload ala Flickr. Vznik bych klidně finančně nebo recipročně podpořil.

muta
Člen | 21
+
0
-

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
+
0
-

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
+
0
-

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
+
0
-

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
+
0
-

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
+
0
-

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?)

  1. Velikost neřeší, stále je to klasický upload souboru pro PHP, tzn. je potřeba nastavit příslušnou direktivu v PHP
  2. 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
  3. 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
+
0
-

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
+
0
-

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
+
0
-

Ještě dodám, Google Gears si můžete nainstalovat z http://gears.google.com/

Vyki
Člen | 388
+
0
-

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

Honza Kuchař
Člen | 1662
+
0
-

O tomto problému vím. Je to nějaká Windows x Lixux wtf?!

Vyki
Člen | 388
+
0
-

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
+
0
-

Zkus MultipleFileUpload.php upravit podle tohoto: https://forum.nette.org/…iewtopic.php?….

Honza Kuchař
Člen | 1662
+
0
-

@Panda: Díky opraveno.


Problémy s IE:

  1. flash ve <form>u je problém. IE si to přeloží jako window.id.fce(..);. Ale muselo by to být window.form[x].id.fce(...). Řešení: pužívat patchlý swfobject.js (je v distribuci) Ten objekt flashe „zkopíruje“ do objektu window. (do toho co si vygenruje nejde zasahovat :( )
  2. 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 typu frmform-test-text.fce(...). Tzn. odečítá od objektu frmform 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.
  3. 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
+
0
-

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
+
0
-

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
+
0
-

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
+
0
-

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…).

Honza Kuchař
Člen | 1662
+
0
-

To máš pravdu.

demonic
Člen | 19
+
0
-

Zdravím,

Plánujete verzi se zafixovanými jmennými prostory?
Pokoušel jsem se to předělat sám, vše fungovalo až na pár
drobností jako: soubory se neuploadovaly a semtam vyskočil runtime
error a spadl apache.

Za případné reakce předem děkuji.

demonic
Člen | 19
+
0
-

Tohle jsem našel ale obsahuje to chyby, které nové revize řeší + přibyly interface a podobně.

Proto se ptám, jestli se někomu nepodařilo „oživit“ novější revizi se jmennýmí prostory.

Já nebyl úspěšný.

Díky