Скачивание файлов по временным ссылкам
02.10.2010
Теги: HTTP • MySQL • PHP • Web-разработка • Файл
Наверное каждому приходилось сталкиваться с временными ссылками при скачивании фильмов, музыки, программ и т.п. Зачем это делается? Да чтобы другие сайты не размещали ссылки на файлы, которые расположены на нашем сайте. Давайте посмотрим, как написать скрипт, который будет генерить временные ссылки.
Для хранения информации о файлах и временных ссылках будем использовать БД. Таблица 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 ); } ?>
- Загрузка файла на сервер без использования формы
- Как отдать пользователю файл скриптом
- Как узнать сколько раз скачали файл?
- Блог на Laravel 7, часть 17. Временная зона для пользователей, деплой на хостинг TimeWeb
- Блог на Laravel 7, часть 16. Роль нового пользователя, сообщение админу о новом посте
- Блог на Laravel 7, часть 15. Восстановление постов, slug для категории, поста и страницы
- Блог на Laravel 7, часть 14. Валидация данных и права доступа при загрузке изображений
Поиск: HTTP • MySQL • PHP • Web-разработка • Файл