Скрипт «Скачать файл»

Здесь рассмотрим различные варианты отправки файла пользователю "на лету". Т.е. пользователь не сам забирает лежащий на вашем сервере файл, а мы отдаем пользователю файл с помощью PHP-скрипта. Простейший пример отправки файла с формированием полного заголовка выглядит так:

Header("HTTP/1.1 200 OK");
Header("Connection: close");
Header("Content-Type: application/octet-stream");
Header("Accept-Ranges: bytes");
Header("Content-Disposition: Attachment; filename=filename.dat");
Header("Content-Length: 50000");

readfile('MyFile.dat');

Здесь функция readfile читает файл и записывает его в буфер вывода.

Рассмотрим более сложный вариант скрипта с ограничением скорости скачивания:

$fname=$_GET['fname'];
$fsize=filesize('secret_data/'.$fname);

Header("HTTP/1.1 200 OK");
Header("Connection: close");
Header("Content-Type: application/octet-stream");
Header("Accept-Ranges: bytes");
Header("Content-Disposition: Attachment; filename=".$fname);
Header("Content-Length: ".$fsize);

// Открыть файл для чтения и отдавать его частями
$f=fopen('secret_data/'.$fname,'r');
while (!feof($f)) {
  // Если соединение оборвано, то остановить скрипт
  if (connection_aborted()) {
    fclose($f);
    break;
  }
  echo fread($f,10000);
  // Пазуа в 1 секунду. Скорость отдачи 10000 байт/сек
  sleep(1);
}
fclose($f);

Вариант скрипта скрипта для скачивания файла с поддержкой докачки:

$fname=$_GET['fname'];
$fsize=filesize('secret_data/'.$fname);
$fdown='secret_data/'.$fname;

// Установлена или нет переменная HTTP_RANGE
if (@getenv('HTTP_RANGE')=="") {
  // Читать и отдавать файл от самого начала
  $f=fopen($fdown, 'r');

  header("HTTP/1.1 200 OK");
  header("Connection: close");
  header("Content-Type: application/octet-stream");
  header("Accept-Ranges: bytes");
  header("Content-Disposition: Attachment; filename=".$fname);
  header("Content-Length: ".$fsize);

  while (!feof($f)) {
    if (connection_aborted()) {
      fclose($f);
      break;
    }
    echo fread($f, 10000);
    sleep(1);
  }
  fclose($f);
}
else {
  // Получить значение переменной HTTP_RANGE
  preg_match ("/bytes=(\d+)-/", getenv('HTTP_RANGE'), $m);
  $csize=$fsize-$m[1];  // Размер фрагмента
  $p1=$fsize-$csize;    // Позиция, с которой начинать чтение файла
  $p2=$fsize-1;         // Конец фрагмента

  // Установить позицию чтения в файле
  $f=fopen($fdown, 'r');
  fseek ($f, $p1);

  header("HTTP/1.1 206 Partial Content");
  header("Connection: close");
  header("Content-Type: application/octet-stream");
  header("Accept-Ranges: bytes");
  header("Content-Disposition: Attachment; filename=".$fname);
  header("Content-Range: bytes ".$p1."-".$p2."/".$fsize);
  header("Content-Length: ".$csize);

  while (!feof($f)) {
    if (connection_aborted()) {
      fclose($f);
      break;
    }
    echo fread($f, 10000);
    sleep(1);
  }
  fclose($f);
}

C учетом ососбенностей браузеров:

function send_download($data, $filename)
{
  header("HTTP/1.1 200 OK");
  header("Content-Type: application/force-download; charset=utf-8");
  header('Expires: ' . gmdate('D, d M Y H:i:s') . ' GMT');
  $ua = (isset($_SERVER['HTTP_USER_AGENT']))?$_SERVER['HTTP_USER_AGENT']:'';
  $isMSIE = preg_match('@MSIE ([0-9].[0-9]{1,2})@', $ua);
  if ($isMSIE)
  {
      header('Content-Disposition: attachment; filename="' . $filename . '"');

        header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
      header('Pragma: public');
  }
  else
  {
      header('Content-Disposition: attachment; filename="' . $filename . '"');
      header('Pragma: no-cache');
  }
  echo $data;
}

.