Пишем функцию скачки файлов (25кб!) (Рейтинг: +15)

Печать RSS
Всем привет hello .

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

Начнем с простого способа. Пусть наш скрипт получает имя файла через какой-либо из параметров запроса. Это может быть реально набранный URL, а может быть и переписанный сервером при помощи mod_rewrite. Скрипт вызывает функцию file_download () с параметром $filename. Кроме прямой передачи в запросе $filename может также вычисляться на основе исходного идентификатора из запроса или дополняться путем в дереве папок сервера.

Самый легкий способ обработки запросов к скачиваемым файлам — это простое перенаправление на них.
<?php
function file_download ($filename) {
// Проверяем существование файла
  if (file_exists ($filename)) {
// Здесь пишем код, который будет обрабатывать каждую загрузку файла.

//  Перенаправляем клиента на файл.
    header ('Location: ' . $filename);
  } else {
// Если файл не найден, сообщаем клиенту об этом через заголовки HTTP
    header ($_SERVER["SERVER_PROTOCOL"] . ' 404 Not Found');
    header ('Status: 404 Not Found');
  }
// Прерываем дальнейшее выполнение скрипта, чтобы не отправлять мусор в ответе клиенту
  exit;
}
?>

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

Для этого потребуется усложнить нашу функцию закачки.
<?php
function file_download ($filename, $mimetype='application/octet-stream') {
  if (file_exists ($filename)) {
// Отправляем требуемые заголовки
    header ($_SERVER["SERVER_PROTOCOL"] . ' 200 OK');
// Тип содержимого. Может быть взят из заголовков полученных от клиента
// при закачке файла на сервер. Может быть получен при помощи расширения PHP Fileinfo.
    header ('Content-Type: ' . $mimetype);
// Дата последней модификации файла       
    header ('Last-Modified: ' . gmdate ('r', filemtime ($filename)));
// Отправляем уникальный идентификатор документа,
// значение которого меняется при его изменении.
// В нижеприведенном коде вычисление этого заголовка производится так же,
// как и в программном обеспечении сервера Apache
    header ('ETag: ' . sprintf ('%x-%x-%x', fileinode ($filename), filesize ($filename), filemtime ($filename)));
// Размер файла
    header ('Content-Length: ' . (filesize ($filename)));
    header ('Connection: close');
// Имя файла, как он будет сохранен в браузере или в программе закачки.
// Без этого заголовка будет использоваться базовое имя скрипта PHP.
// Но этот заголовок не нужен, если вы используете mod_rewrite для
// перенаправления запросов к серверу на PHP-скрипт
    header ('Content-Disposition: attachment; filename="' . basename ($filename) . '";');
// Отдаем содержимое файла
    echo file_get_contents ($filename);
  } else {
    header ($_SERVER["SERVER_PROTOCOL"] . ' 404 Not Found');
    header ('Status: 404 Not Found');
  }
  exit;
?>
Теперь мы можем скрыть реальный адрес файла, взять его из папки, недоступной для браузеров и программ закачки. Недостатком данной функции является полная загрузка всего файла в память, что при больших его размерах может привести к ее переполнению. Чтобы избежать этой проблемы, можно считывать файл блоками небольшого размера.
<?php
function file_download ($filename, $mimetype='application/octet-stream') {
  if (file_exists ($filename)) {
    header ($_SERVER["SERVER_PROTOCOL"] . ' 200 OK');
    header ('Content-Type: ' . $mimetype);
    header ('Last-Modified: ' . gmdate ('r', filemtime ($filename)));
    header ('ETag: ' . sprintf ('%x-%x-%x', fileinode ($filename), filesize ($filename), filemtime ($filename)));
    header ('Content-Length: ' . (filesize ($filename)));
    header ('Connection: close');
    header ('Content-Disposition: attachment; filename="' . basename ($filename) . '";');
// Открываем искомый файл
    $f=fopen ($filename, 'r');
    while (!feof ($f)) {
// Читаем килобайтный блок, отдаем его в вывод и сбрасываем в буфер
      echo fread ($f, 1024);
      flush ();
    }
// Закрываем файл
    fclose ($f);
  } else {
    header ($_SERVER["SERVER_PROTOCOL"] . ' 404 Not Found');
    header ('Status: 404 Not Found');
  }
  exit;
}
?>

В своем скрипте (скачать) я использовал немного другую функцию:
    <?php
    function downloadFile($filename, $name, $mimetype = 'application/octet-stream')
        {
            if (!file_exists($filename))
                die('Файл не найден');
            $from = $to = 0;
            $cr = null;
            if (isset($_SERVER['HTTP_RANGE'])) {
                $range = substr($_SERVER['HTTP_RANGE'], strpos($_SERVER['HTTP_RANGE'], '=') + 1);
                $from = strtok($range, '-');
                $to = strtok('/');
                if ($to > 0)
                    ++$to;
                if ($to)
                    $to -= $from;
                header('HTTP/1.1 206 Partial Content');
                $cr = 'Content-Range: bytes ' . $from . '-' . (($to) ? ($to . '/' . $to + 1) :
                    filesize($filename));
            } else
                header('HTTP/1.1 200 Ok');
            $etag = md5($filename);
            $etag = substr($etag, 0, 8) . '-' . substr($etag, 8, 7) . '-' . substr($etag, 15,
                8);
            header('ETag: "' . $etag . '"');
            header('Accept-Ranges: bytes');
            header('Content-Length: ' . (filesize($filename) - $to + $from));
            if ($cr)
                header($cr);
            header('Connection: close');
            header('Content-Type: ' . $mimetype);
            header('Last-Modified: ' . gmdate('r', filemtime($filename)));
            $f = fopen($filename, 'r');
            header('Content-Disposition: attachment; filename="' . $name . '";');
            if ($from)
                fseek($f, $from, SEEK_SET);
            if (!isset($to) or empty($to)) {
                $size = filesize($filename) - $from;
            } else {
                $size = $to;
            }
            $downloaded = 0;
            while (!feof($f) and !connection_status() and ($downloaded < $size)) {
                echo fread($f, 512000);
                $downloaded += 512000;
                ob_flush();
                flush();
            }
            fclose($f);
        }
?>
Но в принципе метод тот же smile
---
Пишем комментарии, кто научился чему-то новому или просто понравилась статья smile
Добавил:
Рейтинг: +15
Просмотры: 3066
Комментарии (14) »