PHP APC Tutorial ? cz.4: Upload plików i pasek postępu (file upload progressbar) w PHP+AJAX+APC+jQuery

PHP standardowo nie oferuje możliwości śledzenia postępu w załadowywaniu plików na serwer poprzez formularza HTML. Dzięki rozszerzeniu APC taka możliwość się pojawia. Od razu na początku nadmienię, że nie udało mi się zmusić APC, aby poprawnie współpracowało z uploadem plików na systemie Windows 7 i WAMPie, udało mi się to zrobić, natomiast, na Linuxie (Fedora 15 i LAMPie).

Zacznijmy od konfiguracji. Opcje konfiguracyjne, na które powinniśmy zwrócić uwagę pokazane są poniżej. Niektóre z nich uruchamiają mechanizm uploadu dla APC, a pozostałe definiują pewne maksymalne dopuszczalne rozmiary. Wstawiamy je w pliku konfiguracyjnym php.ini, w dowolnym miejscu, a ich dokładną wartość ustalamy według potrzeb.

apc.rfc1867=on
apc.rfc186_name=APC_UPLOAD_PROGRESS
apc.rfc186_prefix=upload_
apc.max_file_size=200M

post_max_size=200M
upload_max_filesize=200M

Teraz przechodzimy do pisania kodu. Będziemy potrzebować bibliotekę jQuery, która będzie dołączona do naszych plików. My za to utworzymy 5 plików. Aż 5, by wszystko było przejrzyste i podzielone pod względem funkcjonalnym. Najpierw plik formularza uploadForm.php, na którym startujemy.

<?php
	include_once( 'uploadClass.php' );
	$uid = uploadProgress::generateUID();
?>
 
<html>
<head>
	<script type="text/javascript" src='jquery-1.6.4.js'></script>
	<script type="text/javascript" src='uploadScript.js'></script>
</head>
 
<body>
	<h2>Pasek postępu ładowania pliku w PHP przy użyciu APC i jQuery</h2>
	<form onSubmit="startUpload('<?php echo $uid ?>');" action="uploadDone.php" method="post" enctype="multipart/form-data" id="upload_form" target="upload_frame">
		<input type="hidden" name="APC_UPLOAD_PROGRESS" id="upload_uid" value="<?php echo $uid ?>" />
		<input type="file" name="upload_file" id="upload_file" />
		<input type="submit" name="upload_submit" id="upload_submit" value="Upload!" />
	</form>
	<iframe style="display: none" id="upload_frame" name="upload_frame"></iframe>
	<div id="upload_content"></div>
</body>
</html>

W większej części jest to bardzo prosty i zwykły formularz w HTML do uploadu plików. Opiszę teraz to co nie jest już takie oczywiste. Na samej górze dołączmy plik php z klasą uploadProgress, którą napiszemy sobie jako następną. Używamy jej do wygenerowania unikalnego identyfikatora pliku, który chcemy załadować. Dalej dołączamy bibliotekę jQuery i plik z kodem JavaScript, który również opiszemy trochę dalej. W dalszej części mamy formularz, który wywołuje funkcję startUpload(uid) podczas zatwierdzenia formularza (funkcje tą zdefiniujemy właśnie we wspomnianym pliku JS). Co ważne – żeby strona nam się nie przeładowywała, targetem dla tego formularza jest ukryty iframe zdefiniowany pod formularzem. Jednym z pól formularza jest pole APC_UPLOAD_PROGRESS. Pole te musi być pierwszym polem formularza, a służy do tego, żeby uruchomić mechanizm uploadu w APC. W tym polu przekazujemy też wygenerowany identyfikator.

Teraz kolejny plik – uploadClass.php – zawierający wspomnianą już prostą klasę.

<?php
	class uploadProgress {
		public static function generateUID() {
			return uniqid( rand() );
		}
 
		public static function getPercentage( $uid ) {
			$status = apc_fetch( 'upload_' . $uid );
			$percentage = round( $status['current'] / $status['total'] * 100 );
			return $percentage;
		}
	}
?>

Klasa jest bardzo prosta. Zawiera tylko dwie metody. Pierwsza służy do generowania unikalnych identyfikatorów dla naszych plików. Druga metoda służy do zwrócenia na podstawie identyfikatora informacji o tym, jaka część pliku już została załadowana.

Trzeci plik to również wspomniany już plik z kodem JavaScript – uploadScript.js.

function startUpload( uid ) {
	getProgress( uid );
	$('#upload_form').hide();
}
 
function getProgress( uid ) {
	$.ajax({
		url: "uploadStatus.php?uid=" + uid,
		success: function( data ) {
			var percentage = parseInt( data );
			if( percentage < 100 ) {
				$('#upload_content').html( percentage + '%' );
				setTimeout('getProgress("' + uid + '")', 1000);
			}
			else
				finishUpload();
		}
	});
}
 
function finishUpload() {
	$('#upload_content').html( 'Ładowanie zakończone!' );
}

Zdefiniowane mamy tu trzy funkcje. Pierwsza wykonywana przy zatwierdzaniu formularza – ukrywa ona formularz (ważne jest to, że należy tylko ukryć formularz, bo jego usunięcie przerwie upload) oraz wywołuje drugą funkcję. Druga funkcja cyklicznie (ale do czasu pełnego załadowania pliku) za pomocą AJAXa odczytuje zawartość pliku uploadStatus.php, (o którym za chwile) który to podaje jaka część pliku już została załadowana. Wartość tą wpisujemy do zdefiniowane w pierwszym pliku DIVa. Kiedy osiągniemy 100% to wywoływana jest trzecia metda, która po prostu zamienia procenty na odpowiedni komunikat.

Kolejny plik to uploadStatus.php.

<?php
	include_once( 'uploadClass.php' );
	$uid = $_GET['uid'];
	echo uploadProgress::getPercentage( $uid );
?>

Ten mały plik służy do zwracania informacji o postępie załadowania pliku. Wykorzystuje on opisany już mechanizm drugiej metody w wyżej opisanej klasie.

Ostatni – piąty – plik to uploadDone.php, który jest wrzucany do niewidocznego iframe’a, a który służy do wykonania kodu po załadowaniu pliku.

<?php
	if($_FILES['upload_file']['error'] == UPLOAD_ERR_OK) {
		/*
		KOD DO WYKONANIA PO ZALADOWANIU PLIKU
		*/
	}
?>