Форум: Форум PHPФорум ApacheФорум Регулярные ВыраженияФорум MySQLHTML+CSS+JavaScriptФорум FlashРазное
Новые темы: 0000000
PHP Puzzles. Авторы: Кузнецов М.В., Симдянов И.В. PHP 5/6. В подлиннике. Авторы: Кузнецов М.В., Симдянов И.В. Самоучитель PHP 5 / 6 (3 издание). Авторы: Кузнецов М.В., Симдянов И.В. PHP. Практика создания Web-сайтов (второе издание). Авторы: Кузнецов М.В., Симдянов И.В. Самоучитель MySQL 5. Авторы: Кузнецов М.В., Симдянов И.В.
ВСЕ НАШИ КНИГИ
Консультационный центр SoftTime

Форум PHP

Выбрать другой форум

 

Здравствуйте, Посетитель!

вид форума:
Линейный форум (новые сообщения вниз) Структурный форум

тема: Скачка файлов с возможностью докачки

Сообщения:  [1-10]    [11-20]   [21-30]  [31-33] 

 
 автор: Лена   (02.03.2009 в 12:56)   письмо автору
12.1 Кб
 
   для: Trianon   (01.03.2009 в 17:45)
 

> неплохо его еще и на функции делить.
По этому поводу около недели назад был разговор с sim`ом (не на форуме, в привате). В двух словах смысл разговора сводился к тому, что функция - это подпрограмма, после выполнения которой нужно вернуться к основной программе (по адресу, который заносится в стек). Получается такой вот "скачок" во внутренней памяти, поэтому если можно его избежать, функции лучше не использовать. В этом скрипте функции необязательны.

А теперь код. У меня в файле все красиво, все отступы, а здесь искажаются. Пробовала сделать, как надо. На всякий случай нормальный вариант прикрепила.

<?php
include_once("configs/dbopen.php");
//получаем нужную книгу по ссылке
if(isset($_GET['id_book'])){
        
//выбираем название книги, путь и расширение файла из базы
        
$sql "SELECT * FROM books WHERE id =" $_GET['id_book'];
        
$res mysql_query($sql);
                    if(!
$res) exit ("Error in $sql:" mysql_error());
                        while(
$row mysql_fetch_assoc($res)){
                        
//print_r($row);
                                //определяем расширение
                                
$extension $row['type'];
                                
$ext = array('zip''rar''gz''tgz''jpg','jpe','jpeg''gif''png''txt''pdf', );
                                
$stream = array('application/x-zip''application/x-rar-compressed''application/x-gzip','application/x-gzip','image/jpeg','image/jpeg','image/jpeg','image/gif''image/png''text/plain''application/pdf');
                                    
$arr array_combine($ext,$stream);
                                        foreach(
$arr as $k=>$v){
                                            if(
$extension == $k$mime $v;
                                            else 
$mime 'application/octet-stream';
                                        }
                                        
$filename $row['path'] . "/"$row['title'] . "." $row['type'];

//проверка на существование и воспроизведение нужного файла
if (!file_exists($filename) && !is_readable($filename)) exit('File does not exist or don`t readable');

//сервер сообщает клиенту, что непротив обслуживать байт-диапазонные запросы
header('Accept-Ranges: bytes');

    
$f fopen($filename'rb');
       
//проверка, открылся ли файл
        
if(!$f){
        
header ("HTTP/1.1 505 Internal server error");
        exit();
        }
    
flock($fLOCK_EX);
                                        
    
fseek($f0SEEK_END); //указатель - в конец файла, получаем позицию указателя
    
$size ftell($f);
                                        
    
//метка объекта, хеш всего файла, md5_file($filename)
    //выбираем из базы, вычисляем при загрузке данных про файл в базу
    
$etag_server $row['hash'];
                                        
    
//время последней модификации файла
    
$last_mod filemtime($filename);
                                        
    
//клиент запросил докачку
    
if (isset($_SERVER['HTTP_RANGE'])) {
        
//проверяем, что лежит в $_SERVER['HTTP_RANGE']: bytes=20-50/длина файла
            
if (!preg_match('/^bytes=(?:\d+-\d+|-\d+|\d+-//\d+)$/'$_SERVER['HTTP_RANGE'])){
                
//если range некорректный, посылаем весь текст
                
header("HTTP/1.1 416 Requested Range Not Satisfiable");
                
header("HTTP/1.1 200 OK");
                
header('Content-Type: '$mime);
                
header('Content-Disposition: attachment; filename=' basename($filename));
                
header('Content-Length:' $size);
                
header ("Last-Modified: " gmdate("D, d M Y H:i:s \G\M\T"$last_mod));
                
header("Expires: " gmdate("D, d M Y H:i:s \G\M\T"time()+60*10));
                }
                                            
                
// условные запросы 
                
if(isset($_SERVER['HTTP_IF_RANGE'])){
                
//определяем, чем являются первые символы - меткой или датой
                
$ifr trim($_SERVER['HTTP_IF_RANGE']); 
                
$iftag $ifdate '';
                    if(
ctype_digit($ifr)){
                    
$ifdate strtoftime($ifr);
                    
//если это дата, проверяем время модификации
                    
if(isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])){
                    
$ifmod strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']);
                        if(
$ifdate == $ifmod && $ifmod == $last_mod){
                            
header("HTTP/1.1 304 Not Modified"); //файл не поменялся
                            
header("ETag: \""$etag_server ."\"");
                            
// последнее изменение
                            
header ("Last-Modified: " gmdate("D, d M Y H:i:s \G\M\T"$last_mod));
                            
//страница не будет меняться 10 минут
                            
header("Expires: " gmdate("D, d M Y H:i:s \G\M\T"time()+60*10));
                            exit();
                        }else{
                        if(
$ifmod $last_mod){
                        
//файл изменился, присваиваем метку
                        
header("ETag: \""$etag_server ."\"");
                        
// последнее изменение
                        
header ("Last-Modified: " gmdate("D, d M Y H:i:s \G\M\T"$last_mod));
                        
//страница не будет меняться 10 минут
                        
header("Expires: " gmdate("D, d M Y H:i:s \G\M\T"time()+60*10));
                        }
                         }
                        }
                        }else{
                        
$iftag $ifr;
                        
//если это метка, проверяем, совпадает ли метка объекта с текущей
                            
if(isset($_SERVER['HTTP_IF_MATCH']) && $_SERVER['HTTP_IF_MATCH'] != "*"){
                                
$etag_client trim($_SERVER['HTTP_IF_MATCH']);
                                
//метка не совпала
                                    
if($iftag == $etag_client && $etag_client !== $etag_server){
                                        
//присылаем полный объект
                                        
header("HTTP/1.1 200 OK");
                                        
header('Content-Length:' $size);
                                        
//надо присвоить новую метку
                                        
header("ETag: \""$etag_server ."\"");
                                        
// зафиксировать последнее изменение
                                        
header ("Last-Modified: " gmdate("D, d M Y H:i:s \G\M\T"$last_mod));
                                        
//страница не будет меняться 10 минут
                                        
header("Expires: " gmdate("D, d M Y H:i:s \G\M\T"time()+60*10));
                                    }else{
                                        
//метка совпала, значит, файл не менялся    
                                        
header("HTTP/1.1 304 Not Modified");
                                        
header("ETag: \""$etag_server ."\"");
                                        
// последнее изменение
                                        
header ("Last-Modified: " gmdate("D, d M Y H:i:s \G\M\T"$last_mod));
                                        
//страница не будет меняться 10 минут
                                        
header("Expires: " gmdate("D, d M Y H:i:s \G\M\T"time()+60*10));
                                    }
                                }
                            }
//if(ctype_digit
                        
}else{
                        
//условных запросов нет, заголовок - предварит. усл. не выполнены
                        
header("HTTP/1.1 412 Precondition Failed");
                        
header("ETag: \""$etag_server ."\"");
                        
// последнее изменение
                        
header ("Last-Modified: " gmdate("D, d M Y H:i:s \G\M\T"$last_mod));
                        
//страница не будет меняться 10 минут
                        
header("Expires: " gmdate("D, d M Y H:i:s \G\M\T"time()+60*10));
                        exit();    
                        }
                                                
                        
//определяем диапазон
                        
$range=substr($_SERVER['HTTP_RANGE'], strpos($_SERVER['HTTP_RANGE'], '=')+1);
                        
$range substr($rangestrpos($range,0,"/"));
                           
$range explode("-"$range);
                        
$from $range[0];
                        
$to $range[1];
                        
//если первая позиция не задана, берем ее с нуля
                        
if($from 0$from 0;
                        if(
$to$to = ++ $to;
                        
//если не задана позиция конца
                        
else $to $size;
                                                                    
                        
//от позиции и до конца, диапазон типа 521000-
                            
if($range[1]==''){
                                
$from $range[0];
                                
$to $size;
                            }
                        
//последние байты, диапазон типа -300000
                            
if($range[0]==''){
                                
$from $range[1];
                                
$to $size;
                            }
                                                            
                        
//узнаем, запросил клиент полный файл либо кусок
                            
if ($from || $to $size) {
                            
//первый и последний байт куска
                            
$start $from;
                            
$end $to 1;
                            
header('HTTP/1.1 206 Partial Content');
                                                                
                            
//длина диапазона, который считывается
                            
$part $to $from;
                            
header('Content-Length:' $part);
                            
header('Content-Range: bytes ' $start "'-'" $end "'/'" $size);
                            
header("ETag: \""$etag_server ."\"");
                            
// последнее изменение
                            
header ("Last-Modified: " gmdate("D, d M Y H:i:s \G\M\T"$last_mod));
                            
//страница не будет меняться 10 минут
                            
header("Expires: " gmdate("D, d M Y H:i:s \G\M\T"time()+60*10));
                            }else{
                           
// если клиент не запросил докачку или byte-range-spec некорректна
                            
header("HTTP/1.1 200 OK");
                            
header('Content-Length:' $size);
                            
header("ETag: \""$etag_server ."\"");
                            
// последнее изменение
                            
header ("Last-Modified: " gmdate("D, d M Y H:i:s \G\M\T"$last_mod));
                            
//страница не будет меняться 10 минут
                            
header("Expires: " gmdate("D, d M Y H:i:s \G\M\T"time()+60*10));
                            }
                                }else{
                                    
//диапазон неопределен, значит, клиент запросил весь файл
                                    
header("HTTP/1.1 200 OK");
                                       
header('Content-Length:' $size);
                                    
header("ETag: \""$etag_server ."\"");
                                    
// последнее изменение
                                    
header ("Last-Modified: " gmdate("D, d M Y H:i:s \G\M\T"$last_mod));
                                    
//страница не будет меняться 10 минут
                                    
header("Expires: " gmdate("D, d M Y H:i:s \G\M\T"time()+60*10));
                                    
$range = array(0,$size);
                                    
$from $range[0];
                                    
$to $range[1];
                                    
$start $from;
                                    }
                                        
                                        
//отправляем заголовки и для целого файла, и для частичной загрузки
                                        
header('Content-Type: '$mime);
                                        
header('Content-Disposition: attachment; filename=' basename($filename));
                                        
                                        
//ставим указатель на начальную позицию, которую задал клиент
                                                   
fseek($f$startSEEK_SET);
                                                    
//выдача файла
                                                    
while(!feof($f)) {
                                                       if(
$to == $size){
                                                           
//выводим остаток до конца файла
                                                           
fpassthru($f);
                                                       }else{
                                                               
//выводим начало и середину
                                                            
$read fread($f256000);
                                                            echo 
$read;
                                                            
flush();
                                                        }
                                                    }
                                                
flock($fLOCK_UN); 
                                                
fclose($f);
                        }
//while($row =
        //счетчик скачиваний
        
$sql_update "UPDATE books SET count_download = count_download + 1 WHERE id =" $_GET['id_book'];
        
mysql_query($sql_update);
}
//if(isset($_GET
?>

  Ответить  
 
 автор: Trianon   (01.03.2009 в 17:45)   письмо автору
 
   для: Trianon   (01.03.2009 в 17:11)
 

опять же, учтите , что
------------------------------------------------------
13.29. Поле Last-Modified

Поле заголовка объекта Last-Modified указывает на дату и время, при которых, по мнению исходного сервера, данный объект был модифицирован.

Last-Modified = "Last-Modified" ":" HTTP-date

Пример его использования

Last-Modified: Tue, 15 Nov 1994 12:45:26 GMT

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

Исходный сервер не должен посылать дату Last-Modified, которая позже, чем время формирования сообщения сервера. В таких случаях, когда последняя модификация объекта указывает некоторое на время в будущем, сервер должен заменить дату на время формирования сообщения.

Исходный сервер должен получить значение Last-Modified объекта как можно ближе по времени к моменту генерации значения Date отклика. Это позволяет получателю выполнить точную оценку времени модификации объекта, в особенности, если объект был изменен буквально накануне формирования отклика.

Серверы HTTP/1.1 должны посылать поле Last-Modified всякий раз, когда это возможно.
------------------------------------------------------

  Ответить  
 
 автор: Trianon   (01.03.2009 в 17:16)   письмо автору
 
   для: Trianon   (01.03.2009 в 17:11)
 

>Получается, если скрипт большой, надо куски отдельно проверять
Безусловно. неплохо его еще и на функции делить.

Я заметил ошибку по тексту, и не поверив в результат, решил этот фрагмент проверить.

  Ответить  
 
 автор: Trianon   (01.03.2009 в 17:11)   письмо автору
 
   для: Лена   (01.03.2009 в 17:00)
 

2. 32 байта на запись? Мелочь.
Как вариант можно сами файлы держать под именами, равными md5. Или md5 с seq-суффиксом.

6. но изменился-то он именно тогда, когда filemtime, а не позже?

7.
>Если диапазон такой: 521000- , мы должны взять первые байты тела объекта,
нет. см. (22.02.2009 в 22:51)
521000- означает "Взять с позиции 521000 и до конца файла".
И это, кстати 99.9% всех издаваемых диапазонных запросов.

>Аналогичным образом определяла диапазон типа -9000, когда нужно взять последние байты тела объекта.
а здесь верно.
-9000 означает взять последние 9000 байт файла.

  Ответить  
 
 автор: Лена   (01.03.2009 в 17:00)   письмо автору
 
   для: Trianon   (01.03.2009 в 12:34)
 

1. Этот блок не так должен работать. Как должен, сама сделаю и покажу.
Получается, если скрипт большой, надо куски отдельно проверять. А здесь я не правильно проверила. Просто загружала файлы с разными расширениями, а надо было не так делать.

2.
>Лучше бы хранить посчитанное значение хеша прямо в таблице.
В таблице базы? Но если файл большой и таких файлов много? Это ж как базу раздует...

3. С этим согласна.
4. Подумаю и переделаю.
5. Вот до этого не додумалась :)
6.
>Кстати, почему в качестве last-modified выдается текущее время, если файл имеет явное время модификации?
Потому что файл изменился и нужно зафиксировать время, когда он изменился. Изменился сейчас - ставим время на данный момент.

7.
>это как?
Если диапазон такой: 521000- , мы должны взять первые байты тела объекта, тогда начинаем брать с нулевого байта и до 521000, значит $from = 0; $to = $range[0]; - или в этом случае $to = 521000.
if($range[1]==''){ - если элемент массива(который мы получали explode), пустой, то...
Аналогичным образом определяла диапазон типа -9000, когда нужно взять последние байты тела объекта.


Остальное буду исправлять.

  Ответить  
 
 автор: Trianon   (01.03.2009 в 12:34)   письмо автору
 
   для: Trianon   (01.03.2009 в 11:33)
 

7
        //первые байты, диапазон типа 521000-
        if($range[1]==''){
          $from = 0;
          $to = $range[0];
        }

это как?

  Ответить  
 
 автор: Trianon   (01.03.2009 в 12:23)   письмо автору
 
   для: Trianon   (01.03.2009 в 11:33)
 

6.


              header("ETag: \"". $etag_server ."\"");
              // зафиксировать последнее изменение (сейчас)
              header ("Last-Modified: " . gmdate("d M Y H:i:s \G\M\T", time()));
              //страница не будет меняться 10 минут
              header("Expires: " . gmdate("d M Y H:i:s \G\M\T", time()+60*10));


Все эти параметры должны выдаваться в любом заголовке отклика. А не только под условиями.
Кстати, почему в качестве last-modified выдается текущее время, если файл имеет явное время модификации?

Опять же
            header("HTTP/1.1 412 Precondition Failed");
            exit();

перед exit еще по идее должны следовать Content-type и прочие характеристики объекта.

  Ответить  
 
 автор: Trianon   (01.03.2009 в 12:21)   письмо автору
 
   для: Trianon   (01.03.2009 в 11:33)
 

5.
        //определяем, чем являются первые символы - меткой или датой
        if(!preg_match('/^\d{2}\s\d{2}\s\d{4}\s\d{2}:\d{2}:\d{2}\sGMT/',
                            $_SERVER['HTTP_IF_RANGE']))


Слова стандарта "Сервер может отличить корректную дату HTTP от любой формы метки объекта, рассмотрев не более двух символов" - это не повод проверять всю дату на соответствие,
тем более что как клиентская, так и серверная часть должна быть достаточно толлерантна к форматам даты.
на самом деле, поскольку в первых двух буквах названия дня недели встречается хотя бы один из нешестнадцатеричных символов, достаточно написать что-то вроде
$ifr = trim($_SERVER['HTTP_IF_RANGE']);
$iftag = $ifdate = '';
if(ctype_digit($ifr)) $iftag = $ifr; else  $ifdate = strtoftime($ifr); 

  Ответить  
 
 автор: Trianon   (01.03.2009 в 12:04)   письмо автору
 
   для: Trianon   (01.03.2009 в 11:33)
 

4.
      // условные запросы
      if(isset($_SERVER['HTTP_IF_RANGE']) && $_SERVER['HTTP_IF_RANGE'] == $etag_server
                  || strtotime($_SERVER['HTTP_IF_RANGE']) >= $last_mod)

в этом фрагменте strtotime() будет работать снеопределенным элементом HTTP_IF_RANGE массива $_SERVER

что-то тут не так.

  Ответить  
 
 автор: Trianon   (01.03.2009 в 11:56)   письмо автору
 
   для: Trianon   (01.03.2009 в 11:33)
 

3. проверка формата Range:
      if (!preg_match('/^bytes=(?:\d+-\d+|-\d+|\d+-)(?:,(?:\d+-\d+|-\d+|\d+-))*//\d+$/',
                                          $_SERVER['HTTP_RANGE']))
          return false;

заканчивается каким-то return, хотя по идее достаточно всего лишь считать поле Range отсутствующим, и выдать полный контент.
Кроме того, шаблон выглядит ... странно.
В нем явно вырисовываются следы возможности обработки мультидиапазонных запросов, в то время как сам скрипт явно к этому не готов.
При чем именно следы. Синтаксис явно нарушен...
Такое впечатление, что РБНФ прямо воткнута в конец шаблона.

  Ответить  

Сообщения:  [1-10]    [11-20]   [21-30]  [31-33] 

Форум разработан IT-студией SoftTime
Rambler's Top100
вверх

Rambler's Top100 Яндекс.Метрика Яндекс цитирования