Почему не работает загрузчик даже при GET-запросе?

Написал небольшой загрузчик, но почему-то он не работает если не оформлять ссылку.
Но если переходить по ссылке, то работает. И тут возникает вопрос: "А какая разница?" Может кто поможет разобраться, очень уж интересно.

Стартовая страница:
<!DOCTYPE html>
<html>
<head>
<meta charset="windows-1251">
<meta name="viewport" content="width=device-width, user-scalable=no">
<link rel="stylesheet" type="txt/css" href="data/css.css">
<script src="data/js.js"></script>
</head>
<body>
<div id ="buttons">
<?
	$dir = opendir ("files");
	while ($file = readdir($dir)) 
	{
		if(!($file=="."||$file==".."))
		echo "<p>$file</p>"; // Так не скачивает
	}
	echo "</div>";
	echo "<a href=\"download.php?file=17334_5.jpg\">test</a>"; // А так скачивает
?>

</body>
</html>


Обработка загрузок:
<?
if ( isset($_GET['file']) ){
	$file		= $_GET["file"];
	$file_info	= pathinfo($file);
	$file_ext	= $file_info['extension'];
	$file_name	= $file_info['basename'];
	$ignore_ext = array ('php','html','xml','ini','css','js'); // игнорируемые файлы
	if(file_exists("files/".$file) and in_array($file_ext, $ignore_ext)!=1 )
	{
		header('Content-type: application/force-download'); 
		header('Content-Disposition: attachment; filename=' . $file_name . ''); 
		readfile('files/' . $file . '');
		exit();
	}
	else
	{
		exit ("Неправильное имя файла !");
	}
}
else{
	exit('Файл не выбран !!');
}
?>


Скрипт:
function open_event(){
	document.getElementById('buttons').onclick = download;
}
document.addEventListener("DOMContentLoaded", open_event);
function download (button_file){
	name_file = button_file.target.innerHTML
	var xhr = new XMLHttpRequest();
	xhr.open('GET', 'download.php?file='+name_file, true);
	xhr.setRequestHeader('Host', 'homefiles');
	xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
	xhr.onreadystatechange = function() {
		if (xhr.status != 200) {
			// обработать ошибку
			alert('Не 200: ' + xhr.status + ': ' + xhr.statusText ); // пример вывода: 404: Not Found
		}
	}
	xhr.send();
}
  • Вопрос задан
  • 440 просмотров
Решения вопроса 1
Atlant_T
@Atlant_T Автор вопроса
Всё, вопрос решился.

Ранее предложенная конструкция не дает возможности скачивать файл. Точнее файл то скачивается, но в саму страницу, что соответствует принципу безопасности "каждый сайт в своей песочнице". Поэтому можно прибегнуть к хитрости перехода по ссылке.
А именно:
1. Клиент делает запрос на скачивание путем нажатия на кнопку и запуска скрипта.
2. XHR POST-запросом (ранее GET-запросом) отправляет имя файла на /download.php
3. download.php принимает запрос и формирует сессию, которая автоматически заносится в Сookies
- на случай отключенных cookies, через тот же xhr отвечает id сессии, а скрипт сам решит как использовать ответ
4. После ответа xhr скрипт перенаправляет на /download.php, где проверяется сессия и отправляется запрошенный файл
5. Получаемый файл теперь не считается браузером частью страницы, как это было ранее, и он начинает его загрузку

Стартовая страница (index.php)
<!DOCTYPE html>
<html>
<head>
	<meta charset="windows-1251">
	<meta name="viewport" content="width=device-width, user-scalable=no">
	<link rel="stylesheet" type="txt/css" href="data/css.css">
	<script src="data/js.js"></script>
</head>
<body>
	<div id ="buttons">
	<?
		$dir = opendir ("files");
		while ($file = readdir($dir)) // Ищем файлы в нужной директории
		{
			if(!($file=="."||$file==".."))
			echo "<p>$file</p>";
		}
	?>
	</div>
</body>
</html>


Обработка загрузок (download.php)
<?
if(isset($_GET[session_name()])){
	session_id($_GET[session_name()]);
	/*	Если выключены cookie, то придет GET-запрос с нужной сессией	*/
}
session_start(); // Запускаем сессию
if ( isset($_POST['file']) ){
	$file = pathinfo($_POST['file']);
	if ($file['dirname'] != '.') exit ('Error'); // Не даем возможности гулять по директориям
	$_SESSION["file_for_download"] = iconv( "utf-8", "cp1251", $file['basename']);
	/*	iconv() здесь позволяет работать с файлами на кириллице	*/
	exit("?".session_name().'='.session_id()); // Отправляем данные сессии, вдруг cookie не работают
}
if ( isset($_SESSION["file_for_download"]) ){
	$file = $_SESSION["file_for_download"];
	unset($_SESSION["file_for_download"]);
	/*	Удаляем сессионную переменную,
		чтобы при следующем входе в download.php не началась очередная загрузка */
	if(file_exists("files/".$file)){
		header('Content-Type: application/octet-stream');
		header('Content-Transfer-Encoding: binary');
		header('Content-Disposition: attachment; filename=' . $file);
		header('Content-Length: ' . filesize('files/' . $file));
		/*	Эти заголовки исчерпывающие в данной задаче	*/
		readfile('files/' . $file . '');	// Отправка файла чтением
		exit();
	}
	else
	{
		// Действия, если файла нет. Например, отправка файла с ошибкой и извинениями.
		exit ("Неправильный файл: " . $file);
	}
}
header('Location: /');
/*	Если ничего из сценария не подошло, отправляем клиента на главную страницу (вдруг он не туда вошел)	*/
?>


Javascript (js.js)
function open_event(){
	document.getElementById('buttons').onclick = download;
}
document.addEventListener("DOMContentLoaded", open_event);
function download (button_file){
	name_file = button_file.target.innerHTML
	var xhr = new XMLHttpRequest();
	xhr.open('POST', 'download.php', true);
	xhr.overrideMimeType('text/plain; charset=utf-8');
	xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
	xhr.onreadystatechange = function() {
		if (xhr.status != 200) {
			// Обработать ошибку
			alert('Не 200: ' + xhr.status + ': ' + xhr.statusText ); // Например, 404: Not Found
		} else {
			if (xhr.readyState == 4){
				if (xhr.responseText != "Error"){ // Обработка ошибки. У нас кто-то хочет полазить по директориям.
					no_cookie = "";
					if (!navigator.cookieEnabled) {no_cookie = xhr.responseText}; // Если Cookie отключены
					window.location.href = "download.php" + no_cookie; // держим сессию через GET
					/* 	Это не нужно если в PHP включена настройка session.use_trans_sid.
						Также, при включенной настройке, не понадобится
						isset($_GET[session_name()]) в download.php	*/
				} else {
					alert("Ошибка");
				}
			}
		}
	}
	xhr.send('file='+name_file);
}


Полезные статьи:
Создание простой системы авторизации - зачем используют сессии
Подробное описание работы и объяснение механизма сессии
Отдаем файлы эффективно с помощью PHP
Новые возможности XMLHttpRequest2 - не совсем то, но интересно

Полезные ответы:
Проблема с русскими символами в имени файла

Полезные ресурсы:
О XMLHTTPRequest
О php (есть переводы на русский)
Хорошо описаны заголовки HTTP

Статьи не в тему но интересно:
Определение кодировки текста в PHP
Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы