Загрузка файлов с контролем состояния на php и javascript

Контроль состояния загрузки — это так называемый progress bar. В интеренете полно реализаций на том же flash: выглядит это решение красиво и эффектно, но имеет один минус — требует наличия у пользователя установленного flash плеера. Сама по себе необходимость такой фичи на сайте сомнительна, но есть проект, на котором пользователю предоставляется возможность загружать файлы до одного гигабайта. Тут это скорее необходимость. Как же это сделать не прибегая к решению с флеш-загрузкой?! Оказывается, очень просто.

Первое, можно скачать Uber-Uploader: там все делатеся через SSI, при наличии perl на сервере. Но мы же php-программисты, тем более что это уж очень монстрообразный скрипт.

Итак, что нам нужно!

Устанавливаем модуль php_apc. В php.ini добавляем следующие строки:

extension=php_apc.dll
apc.rfc1867 = on

Далее устанавливаем следующие перменные в такие значения:

file_uploads        = On
max_execution_time  = 60
max_input_time      = 7200
memory_limit        = 128M
post_max_size       = 1000M
upload_max_filesize = 1000M

Все, перезапускаем apache.

Простейшее решение на PHP + Javascript:

UploadForm.php

<html>
<head>
	<title>UploadProgress</title>
	<script type="text/javascript">

		function UpdateProgress(){
			document.getElementById("UploadProgress").style.display = "";
			document.getElementById("UploadForm").style.display = "none";
			var e = document.createElement('SCRIPT');
			e.type = 'text/javascript';
			e.src = "JavaScriptInjection.php?DownloadProgressID=" + UploadForm.APC_UPLOAD_PROGRESS.value + "&CachePrevention=" + (new Date()).getTime();
			document.getElementsByTagName('head').item(0).appendChild(e);
			setTimeout("UpdateProgress()", 500);
		}

		function UpdateClient(data) {
			try {
				document.getElementById("ProgressText").innerHTML = "Uploaded " + (data["current"]/1024/1024).toFixed(2) + "MB of " + (data["total"]/1024/1024).toFixed(2) + "MB";
				document.getElementById("ProgressBar").style.width = (data["current"]/data["total"]*100) + "%";
			} catch (e) { null; }
		}

	</script>
</head>
<body>

	<form id="UploadForm" enctype="multipart/form-data" action="ProcessForm.php" method="POST" onsubmit="UpdateProgress();">
		<input type="hidden" name="APC_UPLOAD_PROGRESS" value="<?php echo uniqid() ?>"/>
		<input type="file"   name="up_file_1"/><br>
		<input type="file"   name="up_file_2"/>
		<input type="submit" value="Upload!"/>
	</form>

	<div id="UploadProgress" style="display:none;">
		<span id="ProgressText"></span><br/>
		<div style='width:600px; background:#CCCCCC;'><div id="ProgressBar" style="background-color:#00CC66; width:0%;"></div></div>
	</div>

</body>
</html>

JavaScriptInjection.php

<?php
	if ($e = apc_fetch("upload_{$_GET['DownloadProgressID']}"))
		echo "UpdateClient(" . json_encode($e) . ")";
?>

ProcessForm.php

<?php
	// At this point file upload is complete.
	// move_uploaded_file($_FILES["up_file_1"]["tmp_name"], "c:\\upload\\" . $_FILES["up_file_1"]["name"]);
?>

Загрузка завершена.

Ну вот, данный скрипт будет работать. Сразу скажу пример кода не мой: я реализовал все по-другому: Ajax, подсчет данных и прочее, но скрипт коммерческий, поэтому не могу его выставить на обозрения — то, что оплачивал заказчик остается у заказчика — по крайней мере в паблик не выкладываю, хотя путь указал, да и мои контактные данные тоже имеются на сайте ;).

Преимущества данного решения:

  1. Не требует переустановки или сборки php
  2. Не использует CGI/Perl и другиз сторонних решений
  3. Работает на версиях php 5.2 и выше с установленным модулем php_apc
  4. Не требует от клиента никаких дополнителных примочек к браузеру


Что еще хочу добавить по скрипту: если захотите передалать загрузку и контроль состояния с помощью Ajax то не забывайте

  • убивать таймер
  • javascript c файлами не работает, следовательно Ajax-ом не отправишь форму с файлом — приходится хитрить, способы есть.

Как минус, это обязательное наличие библиотеки, но имея dedicated server, такой минус можно опустить ибо настройка занимает около двух минут.

Загрузка файлов с контролем состояния на php и javascript: 11 комментариев

  1. Современные браузеры (FF 3.6, webkit) поддерживают отправку файлов через XHR с информацией о прогрессе загрузки как по одному файлу, так и по всем.

    • Это здорово, но если учесть что пользователи ИЕ6 не вымерли, а тот же ИЕ7-8 твердо стоит на ногах нельзя пока принебрегать. Кстати данный пример также отображает загрузку нескольких файлов.

    • к сожалению хостинг на котором находится блог не поддерживает php_apc :((

  2. Note that the file upload tracking is not threadsafe at this point, so new uploads that happen while a previous one is still going will disable the tracking for the previous.

    • Согласен, замечание дельное, это нужно также предусматривать.

  3. Спасибо, обязательно попробую.
    Настройте пожалуйста, стили для печати нормальным образом, а то из FireFox 3.6.3 информация распечатывается в виде длинного и узкого столбца читать который не возможно(((

    • Если ваше сообщение не спам, то даже не знаю что сказать 🙂 у меня у самого последний ФФ и все корректно

  4. А как быть с хостингами не поддерживающих php_apc? Может есть какое нибудь альтернативное решение?

    • пока не знаю, на ум приходит только использование CGI

Обсуждение закрыто.