Все ленты — последние статьи

Скачивание файлов по временным ссылкам

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

В качестве хранения информации о файлах и временных ссылках, будем использовать БД. Таблица files хранит информацию о файлах:
CREATE TABLE `files` (
`id` INT(10) PRIMARY KEY,
`title` VARCHAR(255) NOT NULL DEFAULT '',
`description` TEXT NOT NULL DEFAULT '',
`filename` VARCHAR(64) NOT NULL DEFAULT '',
`mimetype` VARCHAR(8) NOT NULL DEFAULT ''
) ENGINE=INNODB DEFAULT CHARSET=cp1251;

Здесь

 

id — уникальный ID файла
title — название файла, например, “Текстовой редактор NotePad++“
description — описание файла, например, “Бесплатный редактор текстовых файлов (замена стандартного Блокнота) с поддержкой синтаксиса большого количества языков программирования, ориентирован для работы в операционной системе MS Windows“
filename — имя файла для скачивания, например, NotePadPP.zip
mimetype — MIME-тип файла

Таблица downloads хранит информацию о временных ссылках:
CREATE TABLE `downloads` (
`file_id` INT(10) NOT NULL DEFAULT 0,
`uniq_id` VARCHAR(32) NOT NULL DEFAULT '',
`puttime` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00'
) ENGINE=INNODB DEFAULT CHARSET=cp1251;

Здесь

file_id — уникальный ID файла
uniq_id — временная ссылка
puttime — время создания ссылки

Файлы для скачивания расположены в директории DOCUMENT_ROOT/download/files/. Эта директория должна быть защищена с помощью .htaccess:
Order Allow,Deny
Deny from All

Скрипт, который будет выполнять всю работу — выводить список файлов, генерить временные ссылки, и отдавать файлы на скачивание — DOCUMENT_ROOT/download/index.php

 

<?php

// Соединяемся с сервером БД
mysql_connect ( 'localhost', 'root', '' );
mysql_query( 'SET NAMES cp1251' );
mysql_select_db ( 'downloads' );

// удаляем устаревшие записи в таблице БД downloads
$query = 'DELETE FROM downloads WHERE puttime < (NOW() — INTERVAL 12 HOUR)';
mysql_query( $query );

$actions = array( 'fileslist', 'getlink', 'download' );

$action = 'fileslist';
if( isset( $_GET['action'] ) and in_array( $_GET['action'], $actions ) ) $action = $_GET['action'];

switch( $action ) {
case 'fileslist': // список файлов для скачивания
fileslist(); break;
case 'getlink': // создаем временную ссылку
getlink(); break;
case 'download': // отдаем файл на скачивание
download(); break;
}

function fileslist() {

echo '<h3>Файлы для скачивания</h3>'."n";
$query = 'SELECT id, title, description, mimetype FROM `files` WHERE 1 ORDER BY title';
$res = mysql_query( $query );

echo '<table border="1">'."n";
echo '<tr><th>№</th><th>Наименование</th><th>Описание</th><th>Тип</th><th>Скачать</th></tr>'."n";
$i = 1;
while( $file = mysql_fetch_array( $res ) ) {
echo '<tr>';
echo '<td>'.$i.'</td>';
echo '<td>'.$file['title'].'</td>';
echo '<td>'.$file['description'].'</td>';
echo '<td>'.$file['mimetype'].'</td>';
echo '<td><a href="'.$_SERVER['PHP_SELF'].'?action=getlink&id='.$file['id'].'">Скачать</a></td>';
echo '</tr>'."n";
$i++;
}
echo '</table>'."n";
}

function getlink() {

// если не передан уникальный ID файла — значит пользователь попал сюда по ошибке
if( !isset( $_GET['id'] ) ) {
header( 'Location: '.$_SERVER['PHP_SELF'].'?action=fileslist' );
die();
}
$id = (int)$_GET['id'];

// прежде чем генерить временную ссылку, проверяем, что есть такая запись в таблице БД
$query = 'SELECT 1 FROM `files` WHERE id='.$id;
$res = mysql_query( $query );
if( mysql_num_rows( $res ) == 0 ) {
header ( 'HTTP/1.1 404 Not Found' );
die();
}

$uniq_id = md5( uniqid(rand(), 1) );
$query = "INSERT INTO downloads (file_id, uniq_id, puttime)
VALUES (".$id.", '".$uniq_id."', NOW())";
mysql_query( $query );

$link = $_SERVER['PHP_SELF'].'?action=download&id='.$id.'&code='.$uniq_id;
echo '<p>Для загрузки файла перейдите по <a href="'.$link.'">этой ссылке</a>. ';
echo 'Ссылка действительна в течение 12 часов.</p>'."n";

}

function download() {

// если не передан уникальный ID файла — значит пользователь попал сюда по ошибке
if( !isset( $_GET['id'] ) ) {
header( 'Location: '.$_SERVER['PHP_SELF'].'?action=fileslist' );
die();
}
$id = (int)$_GET['id'];

if( !isset( $_GET['code'] ) ) {
header( 'Location: '.$_SERVER['PHP_SELF'].'?action=fileslist' );
die();
}

if( !preg_match( '#[a-f0-9]{32}#', $_GET['code'] ) ) {
header ( 'HTTP/1.1 404 Not Found' );
die();
}

$query = "SELECT 1 FROM downloads WHERE file_id=".$id."
AND uniq_id='".$_GET['code']."' AND puttime > (NOW() — INTERVAL 12 HOUR)";
$res = mysql_query( $query );
if( mysql_num_rows( $res ) == 0 ) {
header ( 'HTTP/1.1 404 Not Found' );
die();
}

$query = 'SELECT filename, mimetype FROM `files` WHERE id='.$id;
$res = mysql_query( $query );
if( mysql_num_rows( $res ) == 0 ) {
header ( 'HTTP/1.1 404 Not Found' );
die();
}
list( $filename, $mimetype ) = mysql_fetch_row( $res );

// если файла нет
if( !file_exists( './files/'.$filename ) ) {
header ( 'HTTP/1.1 404 Not Found' );
die();
}

// получаем размер файла
$fsize = filesize( './files/'.$filename );
// дата модификации файла для кеширования
$ftime = date( 'D, d M Y H:i:s T', filemtime( './files/'.$filename ) );
// смещение от начала файла
$range = 0;

// пробуем открыть
$handle = @fopen( './files/'.$filename, 'rb' );

// если не удалось
if( !$handle ){
header ( 'HTTP/1.1 404 Not Found' );
die();
}

// если запрашивающий агент поддерживает докачку
if( $_SERVER['HTTP_RANGE'] ) {
$range = $_SERVER['HTTP_RANGE'];
$range = str_replace( 'bytes=', '', $range );
$range = str_replace( '-', '', $range );
// смещаемся по файлу на нужное смещение
if ( $range ) fseek( $handle, $range );
}

// если есть смещение
if( $range ) {
header( 'HTTP/1.1 206 Partial Content' );
} else {
header( 'HTTP/1.1 200 OK' );
}

header( 'Content-Disposition: attachment; filename="'.$filename.'"' );
header( 'Last-Modified: '.$ftime );
header( 'Content-Length: '.($fsize-$range) );
header( 'Accept-Ranges: bytes' );
header( 'Content-Range: bytes '.$range.'-'.($fsize — 1).'/'.$fsize );

switch( $mimetype ) {
case 'pdf' : $ctype = 'application/pdf'; break;
case 'zip' : $ctype = 'application/zip'; break;
case 'doc' : $ctype = 'application/msword'; break;
case 'xls' : $ctype = 'application/vnd.ms-excel'; break;
case 'gif' : $ctype = 'image/gif'; break;
case 'png' : $ctype = 'image/png'; break;
case 'jpeg':
case 'jpg' : $ctype = 'image/jpg'; break;
case 'mp3' : $ctype = 'audio/mpeg'; break;
case 'wav' : $ctype = 'audio/x-wav'; break;
case 'mpeg':
case 'mpg' :
case 'mpe' : $ctype = 'video/mpeg'; break;
case 'mov' : $ctype = 'video/quicktime'; break;
case 'avi' : $ctype = 'video/x-msvideo'; break;
default : $ctype = 'application/octet-stream';
}

header( 'Content-Type: '.$ctype );

readfile( './files/'.$filename );

fclose( $handle );

}

?>