Visualizzazione risultati 1 fino 6 di 6

Discussione: Upload files in Ajax/PhP

  1. #1
    Guest

    Predefinito Upload files in Ajax/PhP

    Come da titolo sto cercando di implementare l'upload di files, tipicamente immagini, inviando i dati al server con una transazione AJAX e la cosa si sta rivelando più complicata del previsto.

    Premesso che uso praticamente da sempre AJAX (nativo... mi scrivo a manina il codice senza ricorrere a JQuery o altro di simile) non l'ho mai fatto, appunto, per l'upload di files ma solo per interrogare il database.

    Prima di iniziare a scrivere il codice mi sono documentato in rete e praticamente tutti gli esempi cho ho trovato si appoggiano alla classe Javascript "FormData". Il codice che utilizzo è sostanzialmente quello che segue.

    Codice HTML:
    <input type="file" id="uploadFile" name="uploadFile" />
    <br />
    <input type="text" id="uploadDescription" value="" title="Descrizione dell'immagine" />
    <br />
    <input type="button" style="background-color: grey; width: 60px;" value="Invia" onclick="javascript:uploadUserImage();" title="Fai clic qui per inviare l'immagine al server" />
    Il campo con id "uploadFile" apre ovviamente la select files del browser.
    Il campo con id "uploadDescription" consente all'utente di inserire una breve descrizione che vorrei salvare nel database.
    Il butto non effettua il submit, ovviamente, ma invoca la seguente funzione:

    Codice:
    function uploadUserImage(){
    
         // ottengo i valori immessi dall'utente
         var file = document.getElementById("uploadFile");
         var description = document.getElementById("uploadDescription").value;
    
         // istanzio un oggetto della classe FormData
         var formData = new FormData();
    
         // aggiungo al FormData i campi
         formData.append("file", file.files[0]);
         formData.append("description", description);
    
         // dichiaro lo script server-side a cui inviare i dati
         var ajaxFilePath = "./uploadUserImage.php";
    
         // istanzio un oggetto per la trasmissione dati su protocollo HTTP
         var client = new XMLHttpRequest;
         // apro la connessione col server
         client.open('POST', ajaxFilePath, true);
         // informo il server sulla tipologia di dati
         client.setRequestHeader("Content-Type", "multipart/form-data");
         // dichiaro un gestore della risposta che riceverò dal server
         client.onreadystatechange = handleUploadUserImage;
         // trasmetto fisicamente i dati
         client.send(formData);
    
    }
    Lo script PhP che riceve (o meglio... che DOVREBBE ricevere i dati) fa sostanzialmente quanto segue

    Codice PHP:

    // variabile per eventuali messaggi di errore
    $errMsg = "";

    // verifica che sia stato trasmesso il file
    if (!isset($_FILES["file"]) ){
    $errMsg .= "manca il file\n";
    }

    if (!isset(
    $_POST["description"])){
    $errMsg .= "manca la descizione\n";
    }

    // se i dati mancano interrompo l'elaborazione...
    if ($errMsg <> ""){
    die(
    $errMsg);
    }

    // altrimenti proseguo con l'elaborazione
    Lo script termina sempre con il messaggio di errore, visualizzato dall'handler javascript della risposta del server.

    Evito di dettagliare i vari tentativi che ho fatto. Se trasmetto dati di normali campi testo, checkbox o select (impostando diversamente il content-type negli headers) tutto bene, se trasmetto un file niente da fare...

    Il codice che ho postato è molto compatto, in quello reale ci sono tutte le gestioni dello status della transazione, la gestione della risposta, i controlli di sicurezza lato server... ovviamente ho provato anche il codice postato...

    Qualcuno sa dirmi dove sbaglio? grazie!

  2. #2
    karl94 non è connesso Staff AV
    Data registrazione
    03-10-2005
    Messaggi
    17,745

    Predefinito

    Sei riuscito a capire se il problema è lo script PHP o quello JavaScript? Per esempio, hai testato il funzionamento dello script PHP con un normale modulo (HTML puro, senza altro)? Hai poi sbirciato cosa tutto viene inviato nella richiesta POST mediante gli strumenti di sviluppo integrati del browser?

  3. #3
    Guest

    Predefinito

    Grazie Karl94, ti ringrazio. la prova in HTML puro la faccio domani, ma non ho mai avuto problemi in casi analoghi senza usare AJAX... intanto questo è ciò che mi dice Firebug per Firefox... non sono certo di interpretare correttamente, ma quel "[object HTMLInputElement]" non dovrebbe essere il file da inviare?

    Gli altri campi (semplici input di tipo text) sono comunque in ordine ma nemmeno loro vengono "viste" dallo script server side...

    Il tutto in localhost con WAMP...
    Codice:
    Intestazioni di risposta
    Cache-Control	no-store, no-cache, must-revalidate, post-check=0, pre-check=0
    Connection	Keep-Alive
    Content-Length	19
    Content-Type	text/html
    Date	Wed, 12 Mar 2014 20:42:41 GMT
    Expires	Thu, 19 Nov 1981 08:52:00 GMT
    Keep-Alive	timeout=5, max=100
    Pragma	no-cache
    Server	Apache/2.2.22 (Win32) PHP/5.4.3
    X-Powered-By	PHP/5.4.3
    
    Intestazioni di richiesta
    Accept	text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
    Accept-Encoding	gzip, deflate
    Accept-Language	it-IT,it;q=0.8,en-US;q=0.5,en;q=0.3
    Content-Length	475
    Content-Type	multipart/form-data; boundary=---------------------------16469245622173
    Cookie	PHPSESSID=cqqjun7lqfrdiqib207vd5v473
    Host	localhost
    Referer	http://localhost/pastamadre.altervista.org/image.htm
    User-Agent	Mozilla/5.0 (Windows NT 6.0; rv:27.0) Gecko/20100101 Firefox/27.0
    
    Sorgente
    -----------------------------16469245622173 
    Content-Disposition: form-data; name="file"
     
    [object HTMLInputElement] 
    -----------------------------16469245622173
    Content-Disposition: form-data; name="description" 
    
    descrizione 
    -----------------------------16469245622173 
    Content-Disposition: form-data; name="alt"
     
    alt 
    -----------------------------16469245622173
    Content-Disposition: form-data; name="title"
    
    titolo 
    -----------------------------16469245622173--
    Ultima modifica di karl94 : 13-03-2014 alle ore 02.00.34

  4. #4
    karl94 non è connesso Staff AV
    Data registrazione
    03-10-2005
    Messaggi
    17,745

    Predefinito

    Citazione Originalmente inviato da pastamadre Visualizza messaggio
    ma quel "[object HTMLInputElement]" non dovrebbe essere il file da inviare?
    Sì, ma dovrebbe esserci proprio il file, non quella stringa. E poi inviato in quel modo è come se avessi un normale campo di nome file e con valore [object HTMLInputElement]. Però provando il codice che hai fornito nel precedente messaggio il contenuto del file viene correttamente inviato (forse hai fatto qualche modifica, del tipo formData.append("file", file);?). Il problema è però un altro: nell'intestazione Content-Type deve anche comparire il boundary (la stringa che delimita i vari campi). Nella richiesta che hai pubblicato compare, ma provando il codice che hai fornito no (e qua non ho idea di cosa possa avere modificato). Per risolvere il tutto, non impostare quell'header: se leggi le specifiche scopri che quando passi al metodo send un oggetto di tipo FormData, l'header in questione viene costruito in maniera appropriata, e specificarlo a parte lo sovrascriverebbe, omettendo poi quel parametro boundary necessario poi per ottenere i dati correttamente.

  5. #5
    Guest

    Predefinito

    Citazione Originalmente inviato da karl94 Visualizza messaggio
    Sì, ma dovrebbe esserci proprio il file, non quella stringa. E poi inviato in quel modo è come se avessi un normale campo di nome file e con valore [object HTMLInputElement]. Però provando il codice che hai fornito nel precedente messaggio il contenuto del file viene correttamente inviato (forse hai fatto qualche modifica, del tipo formData.append("file", file);?). Il problema è però un altro: nell'intestazione Content-Type deve anche comparire il boundary (la stringa che delimita i vari campi). Nella richiesta che hai pubblicato compare, ma provando il codice che hai fornito no (e qua non ho idea di cosa possa avere modificato). Per risolvere il tutto, non impostare quell'header: se leggi le specifiche scopri che quando passi al metodo send un oggetto di tipo FormData, l'header in questione viene costruito in maniera appropriata, e specificarlo a parte lo sovrascriverebbe, omettendo poi quel parametro boundary necessario poi per ottenere i dati correttamente.
    Karl94 hai ragione, quando ho letto i valori POST con Firebug effettivamente stavo valorizzando il FormData con formData.append("file", file); scusami l'imprecisione.

    Ho rimosso gli headers e di fatto ora, con il codice che posto a seguire, funziona tutto e benissimo.

    Ti ringrazio di nuovo.

    Codice HTML:
    	function uploadUserImage(){
    	
    		// NOTA: l'oggetto "client" viene istanziato dalla classe
    		// nativa XMLHttpRequest esternamente a questa funzione
    		
    		// NOTA: la variabile booleana ajaxIsWorking viene dichiarata e
    		// inizializzata a false esternamente allo script ed e' quindi globale.
    		// Sara' il gestore della risposta del server a rivalorizzarla
    		// con false al termine della elaborazione
    		
    		// se non e' gia' in corso una qualunque transazione AJAX
    		// e se esiste l'oggetto "client"...
    		if (!ajaxIsWorking && client){
    	
    			// ottengo i valori immessi dall'utente
    			var file = document.getElementById("uploadFile");
    			var description = document.getElementById("uploadDescription").value;
    			var keywords = document.getElementById("uploadKeywords").value
    			var alt = document.getElementById("uploadAlt").value
    			var title = document.getElementById("uploadTitle").value
    
    			// istanzio un oggetto dalla classe nativa FormData
    			var formData = new FormData();
    			
    			// aggiungo all'oggetto formData il file scelto dall'utente
    			// e gli altri valori immessi nei vari campi del form
    			formData.append("file", file.files[0]);
    			formData.append("description", description);
    			formData.append("keywords", keywords);
    			formData.append("alt", alt);
    			formData.append("title", title);
    
    			// imposto il percorso per lo script server-side
    			var ajaxPath = "../../../../ajax/";
    			var ajaxScript = "uploadUserImage.php";
    			var ajaxFilePath = ajaxPath + ajaxScript;
    			
    			// NOTA: gli headers necessari vengono generati dal browser
    
    			// apro la connessione
    			client.open('POST', ajaxFilePath, true);
    			// dichiaro il gestore della risposta del server
    			client.onreadystatechange = handleUploadUserImage;
    			// imposto il flag di transazione AJAX in corso
    			ajaxIsWorking = true;
    			// invio fisicamente i dati
    			client.send(formData);
    		
    		}
    
    	}

  6. #6
    Guest

    Predefinito

    Buongiorno, mi sembra un ottimo esempio di uso dell' oggetto formdata. Speravo che ci fosse anche il codice javascript per il progress che, se non erro, dovrebbe permettere la scrittura di una progress bar che non stressa il server con continue richieste del tipo: a che punto sei arrivato? Peccato.
    Per ehm "incoraggiare" i messaggi "didattici" posto un frammento che nelle intenzioni dovrebbe esemplificare tutto il meccanismo browser - server - browser con pochissimo codice. Il metodo è POST senza formdata.
    Sono due files: esAjax.html e AjTest.php
    Il frammento funziona bene qui.

    esAjax.html
    Codice:
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="ISO-8859-1">
    <title>frammento Ajax</title>
    <script type="text/javascript">
    	function AjTest(){
    		var a={};
    		a.testo = document.getElementById('divTest').innerHTML;
    		a.key = 'keyPagina';
    		var toServer = JSON.stringify(a);
    		var xhr = new XMLHttpRequest();
    		xhr.onreadystatechange=function(){
    			if(xhr.readyState==4 && xhr.status==200){
    				document.getElementById('divArrivo').innerHTML = xhr.responseText + '&nbsp;: !Ok ritorno al browser';
    			};
    		}
    		xhr.open('POST', 'AjTest.php');
    		xhr.setRequestHeader("Content-Type", "text/plain");
    		xhr.send(toServer);	
    	}
    </script>
    </head>
    <body>
    	<div style="margin-left: auto; margin-right: auto; margin-top: 25px;width:400px;">
    		<span>Digita un testo:</span>
    		<div id="divTest" style="border: 1px solid;width:300px; height:30px;" contenteditable="true"></div>
    		<br>
    		<button id="buttonTest" style="width:150px; clear: left;" onclick="AjTest();">INVIA</button>
    		<br>
    		<div id="divArrivo" style="color:red; margin-top:20px;"></div>
    	</div>
    </body>
    </html>
    ----------------------

    AjTest.php
    Codice:
    <?php
    header('Cache-Control: no-cache, must-revalidate');
    
    $jarrivo=file_get_contents('php://input');
    $arrivo = json_decode($jarrivo);
    
    echo 'Server arrivo Ok! : &nbsp;<b>' . $arrivo->testo . ' + ' . $arrivo->key . '</b>';

Regole di scrittura

  • Non puoi creare nuove discussioni
  • Non puoi rispondere ai messaggi
  • Non puoi inserire allegati.
  • Non puoi modificare i tuoi messaggi
  •