PHP. Как определить, что пользователь начал закачку файла?

F
На сайте с 09.03.2006
Offline
13
1122

Уважаемые друзья и товарищи! Хотелось бы получить вашу помощь и совет.

Мне нужно реализовать счетчик человек, которые закачали файл...

Сейчас у меня при закачке файла работает такая система - Нажав на название файла, человек попадает на страницу, где срабатывает счетчик ( $downloaded++; ) и записывается в БД. Через секунду методом <META refresh> в который подставляется нужный УРЛ файла пользователю отдается этот файл...

Это не есть удобно для пользователя, да и что-то внутри мне подсказывает, что я слишком все усложнил... Хочу давать прямые ссылки на файлы прямо из каталога (хрен с ними, с личерами - от этого, в конце-концов, только мои пользователи страдают).

Может есть способ сделать счетчик по модели, чтобы скрипт распознавал нажатие ссылки на файл или факт начала самой закачки?

Буду очень благодарен!

_ppr
На сайте с 28.06.2006
Offline
10
#1
feline:

Может есть способ сделать счетчик по модели, чтобы скрипт распознавал нажатие ссылки на файл или факт начала самой закачки?

- можно обрабаывать нажатие на ссылку javascript`ом - при этом отпадает статистика прямых ссылок и выключенный JS

- можно парсить лог апача - самый 100% правильный и надежный результат - но статистика не риалтайм

- можно отдавать файл из php - если файлы большие ненадежно

- можно из php делать редирект - отпадают прямые ссылки (но можно помухлевать с .htaccess и настроить нужный refferer - но обойти можно)

to4kaRU
На сайте с 01.09.2005
Offline
14
#2

Я у себя делаю (упрощенно) следующим образом:

ссылка действительно ведет на отдельный скрипт, в который передается какой-либо параметр, позволяющий определить что за файл нужно отдать на закачку. В моем случае это id записи в БД, в которой лежит имя файла. После того, как скрипт находит имя, делаем следующее:


$file = $path . $filename;

$fname = $filename;

header("Content-Description: File Transfer");
header("Content-Disposition: attachment; filename=\"".$fname."\"");
header("Content-type: application/octet-stream");
readfile($file);

После всего этого пользователю будет выдано окно для сохранения файла.

$file - полный путь к файлу на сервере.

$fname - имя, под которым сохранится файл у пользователя (может отличатся от реального имени файла)

Ну и соответственно в базе увеличиваете счетчик.

--

Действительно, если файл имеет большой размер, надо идти другим путем.

dkameleon
На сайте с 09.12.2005
Offline
386
#3

Пример хтаццесс:

RewriteEngine on
RewriteBase /
RewriteRule ^downloads\/([^/]+)$ get.php?fn=$1 [L]

Пример кода (get.php) с поддержкой докачки:

Вырезал лишние строки, поэтому прийдётся самостоятельно подправить ;)


$fn = @basename($_SERVER["REQUEST_URI"]);

# processing database counter
$db_connection = db_connect_local(DB_HOSTNAME, DB_USERNAME, DB_PASSWORD, DB_DATABASE);
$download = db_get_download($fn);
if (count($download) == 0) {
$download = db_get_latest_download();
}

$fs = @filesize(DIR_DOWNLOADS."/".$download["file_name"]);
if (!$fs) {
header("Location: /");
exit();
}

if (!isset($_SERVER["HTTP_RANGE"])) {
db_increase_download($download["download_id"]);

header ("HTTP/1.1 200 OK");
header ("Expires: Thu, 19 Nov 1981 08:52:00 GMT");
header ("Cache-Control: None");
header ("Pragma: no-cache");
header ("Accept-Ranges: bytes");
header ("Content-Disposition: inline; filename=\"".$download["file_name"]."\"");
if (preg_match("#Opera(/| )([0-9].[0-9]{1,2})#", $_SERVER["HTTP_USER_AGENT"]) or preg_match("#MSIE ([0-9].[0-9]{1,2})#", $_SERVER["HTTP_USER_AGENT"])) {
header("Content-Type: application/octetstream");
} else {
header("Content-Type: application/octet-stream");
}
header ("Content-Length: $fs");
header ("Age: 0");
header ("Proxy-Connection: close");

@readfile(DIR_DOWNLOADS."/".$download["file_name"]);
}else{
ereg("bytes=([0-9]+)", $_SERVER["HTTP_RANGE"], $m);
$contentSize = $fs - intval($m[1]);
$p1 = $fs - $contentSize;
$p2 = $fs - 1;
$p3 = $fs;
$p4= $p3 - $p1;
header ("HTTP/1.1 206 Partial Content");
header ("Expires: Thu, 19 Nov 1981 08:52:00 GMT");
header ("Cache-Control: None");
header ("Pragma: no-cache");
header ("Accept-Ranges: bytes");
header ("Content-Disposition: inline; filename=\"".$download["file_name"]."\"");
if (preg_match("#Opera(/| )([0-9].[0-9]{1,2})#", $_SERVER["HTTP_USER_AGENT"]) or preg_match("#MSIE ([0-9].[0-9]{1,2})#", $_SERVER["HTTP_USER_AGENT"])) {
header("Content-Type: application/octetstream");
} else {
header("Content-Type: application/octet-stream");
}
header ("Content-Range: bytes $p1-$p2/$p3");
header ("Content-Length: $p4");
header ("Proxy-Connection: close");
$fd = @fopen(DIR_DOWNLOADS."/".$download["file_name"], "rb");
@fseek($fd, $p1, SEEK_SET);
while(!@feof($fd)) {
echo(@fread($fd, $p3));
}
@fclose($fd);
}
Дизайн интерьера (http://balabukha.com/)
F
На сайте с 09.03.2006
Offline
13
#4

Dkameleon спасибо большое! Если б еще с комментариями - вообще б цены не было... :))) А зачем докачка? Если у меня бесплатное скачивание, то она же не нужна? Какой там кусок кода можно убрать тогда?

dkameleon
На сайте с 09.12.2005
Offline
386
#5
feline:
А зачем докачка?

Докачка нужна, когда клиент пользователя пытается продолжить закачку после разрыва соединения и запрашивает часть файла, в указанном диапазоне.

Или при многопоточной закачке качалкой.

Поэтому ничего убирать не советую.

Вот тут проверяется, запросил ли пользователь файл с самого начала и целиком, или только определённый диапазон:

if (!isset($_SERVER["HTTP_RANGE"])) {
F
На сайте с 09.03.2006
Offline
13
#6

Dkameleon, вот только сел попытался разобраться с этим скриптом... Не, дофига всего непонятного... Например:

$fn = @basename($_SERVER["REQUEST_URI"]);

Это мы берем какой-то идентификатор, взяв за основу ... имя файла?

# processing database counter

$db_connection = db_connect_local(DB_HOSTNAME, DB_USERNAME, DB_PASSWORD, DB_DATABASE);

с этим, вроде понятно. Но функция, ваша, по-видимому. У меня она называется db_connect(); но это - несущественно.

$download = db_get_download($fn);

А вот этой функции-то у меня нету... Эта функция, скорее всего, извлекает из базы ЧТО-ТО, используя идентификатор полученный на шаге 1 в качестве опознавательного знака... ЧТО она оттуда извлекает, чтобы записать в переменную download?

if (count($download) == 0) {

$download = db_get_latest_download();

}

если эта переменная, в которую мы извлекли непонятно ЧТО равняется нулю... Не, ее еще посчитать надо? Что делает функция count? А db_get_latest_download???

Я тут просто рыдаю - ни фига не понимаю. Точнее, одно понял точно - докачка мне просто необходима! А если с самого начала скрипта не понимаю ничего, то дальше идти - не может быть и речи... HELP! Пожалуйста...

dkameleon
На сайте с 09.12.2005
Offline
386
#7
feline:

Это мы берем какой-то идентификатор, взяв за основу ... имя файла?

Да. Через модреврайт.

При запросе, к примеру:

http://site.com/downloads/file.zip

$_SERVER["REQUEST_URI"] будет содержать /downloads/file.zip

feline:
Эта функция, скорее всего, извлекает из базы ЧТО-ТО, используя идентификатор полученный на шаге 1 в качестве опознавательного знака... ЧТО она оттуда извлекает, чтобы записать в переменную download?

По имени файла вытаскиваем из базы запись, соответствующую этому файлу, если такая есть.

feline:
Что делает функция count?

Проверяет успешность извлечения данных из базы :)

Первую запись, соответствующую запрошенному имени, если такая в базе имеется.

feline:
А db_get_latest_download???

Контроль версий. Если пользователь пытается запросить что-то левое, то ему возвращается самый последний файл. У вас все строки касательно БД можно скорей всего просто выкинуть.

F
На сайте с 09.03.2006
Offline
13
#8
dkameleon:
У вас все строки касательно БД можно скорей всего просто выкинуть.

Спасибо... То есть, первое выкинуть? Оно не имеет отношения к докачке? Значит, нужно оставить только этот кусок?


$fs = @filesize(DIR_DOWNLOADS."/".$download["file_name"]);
if (!$fs) {
header("Location: /");
exit();
}

if (!isset($_SERVER["HTTP_RANGE"])) {
db_increase_download($download["download_id"]);

header ("HTTP/1.1 200 OK");
header ("Expires: Thu, 19 Nov 1981 08:52:00 GMT");
header ("Cache-Control: None");
header ("Pragma: no-cache");
header ("Accept-Ranges: bytes");
header ("Content-Disposition: inline; filename=\"".$download["file_name"]."\"");
if (preg_match("#Opera(/| )([0-9].[0-9]{1,2})#", $_SERVER["HTTP_USER_AGENT"]) or preg_match("#MSIE ([0-9].[0-9]{1,2})#", $_SERVER["HTTP_USER_AGENT"])) {
header("Content-Type: application/octetstream");
} else {
header("Content-Type: application/octet-stream");
}
header ("Content-Length: $fs");
header ("Age: 0");
header ("Proxy-Connection: close");

@readfile(DIR_DOWNLOADS."/".$download["file_name"]);
}else{
ereg("bytes=([0-9]+)", $_SERVER["HTTP_RANGE"], $m);
$contentSize = $fs - intval($m[1]);
$p1 = $fs - $contentSize;
$p2 = $fs - 1;
$p3 = $fs;
$p4= $p3 - $p1;
header ("HTTP/1.1 206 Partial Content");
header ("Expires: Thu, 19 Nov 1981 08:52:00 GMT");
header ("Cache-Control: None");
header ("Pragma: no-cache");
header ("Accept-Ranges: bytes");
header ("Content-Disposition: inline; filename=\"".$download["file_name"]."\"");
if (preg_match("#Opera(/| )([0-9].[0-9]{1,2})#", $_SERVER["HTTP_USER_AGENT"]) or preg_match("#MSIE ([0-9].[0-9]{1,2})#", $_SERVER["HTTP_USER_AGENT"])) {
header("Content-Type: application/octetstream");
} else {
header("Content-Type: application/octet-stream");
}
header ("Content-Range: bytes $p1-$p2/$p3");
header ("Content-Length: $p4");
header ("Proxy-Connection: close");
$fd = @fopen(DIR_DOWNLOADS."/".$download["file_name"], "rb");
@fseek($fd, $p1, SEEK_SET);
while(!@feof($fd)) {
echo(@fread($fd, $p3));
}
@fclose($fd);
}
dkameleon
На сайте с 09.12.2005
Offline
386
#9

Даже так: Вряд ли вам понадобится массив, как у меня:

Дефайните директорию с файлами.

Из запроса по реврайту получаете запрашиваемое имя файла.

Получаете его размер. Если файла не оказывается - редирект в корень и забыли.

В остальных случаях - выдаём файл сначала или с запрошенной части.

        
define("DIR_DOWNLOADS", "files");
$fn = @basename($_SERVER["REQUEST_URI"]);

$fs = @filesize(DIR_DOWNLOADS."/".$fn );
if (!$fs) {
header("Location: /");
exit();
}

if (!isset($_SERVER["HTTP_RANGE"])) {
header ("HTTP/1.1 200 OK");
header ("Expires: Thu, 19 Nov 1981 08:52:00 GMT");
header ("Cache-Control: None");
header ("Pragma: no-cache");
header ("Accept-Ranges: bytes");
header ("Content-Disposition: inline; filename=\"$fn\"");
if (preg_match("#Opera(/| )([0-9].[0-9]{1,2})#", $_SERVER["HTTP_USER_AGENT"]) or preg_match("#MSIE ([0-9].[0-9]{1,2})#", $_SERVER["HTTP_USER_AGENT"])) {
header("Content-Type: application/octetstream");
} else {
header("Content-Type: application/octet-stream");
}
header ("Content-Length: $fs");
header ("Age: 0");
header ("Proxy-Connection: close");

@readfile(DIR_DOWNLOADS."/".$fn);
}else{
ereg("bytes=([0-9]+)", $_SERVER["HTTP_RANGE"], $m);
$contentSize = $fs - intval($m[1]);
$p1 = $fs - $contentSize;
$p2 = $fs - 1;
$p3 = $fs;
$p4= $p3 - $p1;
header ("HTTP/1.1 206 Partial Content");
header ("Expires: Thu, 19 Nov 1981 08:52:00 GMT");
header ("Cache-Control: None");
header ("Pragma: no-cache");
header ("Accept-Ranges: bytes");
header ("Content-Disposition: inline; filename=\"$fn\"");
if (preg_match("#Opera(/| )([0-9].[0-9]{1,2})#", $_SERVER["HTTP_USER_AGENT"]) or preg_match("#MSIE ([0-9].[0-9]{1,2})#", $_SERVER["HTTP_USER_AGENT"])) {
header("Content-Type: application/octetstream");
} else {
header("Content-Type: application/octet-stream");
}
header ("Content-Range: bytes $p1-$p2/$p3");
header ("Content-Length: $p4");
header ("Proxy-Connection: close");
$fd = @fopen(DIR_DOWNLOADS."/".$fn, "rb");
@fseek($fd, $p1, SEEK_SET);
while(!@feof($fd)) {
echo(@fread($fd, $p3));
}
@fclose($fd);
}

Авторизуйтесь или зарегистрируйтесь, чтобы оставить комментарий