Programowanie w języku PHP » FAQ

Jak pobrać plik licząc liczbę ściągnięć

Kod źródłowy tego skryptu pochodzi z projektu Coyote i został trochę przeze mnie zmodyfikowany.

Najprostszy sposób


Najprostszym sposobem jest po prostu przekierowanie skryptu na stronę z plikiem. Np.

header('Location: http://serwer.com/plik.zip');


Wczesniej oczywiście w bazie danych notując ściągnięcie. Ja jednak przedstawię nieco bardziej zaawansowany sposób pozwalający na ukrycie realnej nazwy pliku.

Nieco trudniejszy sposób


Wszystko opiera się na wysłaniu odpowiedniego nagłówka przy pomocy funkcji header().

      header('Pragma: no-cache');
      header('Content-type: application/x-zip-compressed; name="plik.zip"');
      header('Content-Disposition: attachment; filename="plik.zip"');
      header('Content-Length: 1024');


Taki kod spowoduje otwarcie w przeglądarce okienka 'Zapisz jako..'. Przekazane tu nagłówki mówią o tym, że ściągany plik jest typu ZIP</wiki>, jego rozmiar to 1 kB</wiki>, a nazwa to 'plik.zip'. Teraz już wystarczy wysłać do przeglądarki zawartość pliku - np.

readfile('download/plik.zip');


Od tego momentu rozpocznie się ściąganie. Nie polecam jednak stosowania funkcji readfile(). Po pierwsze do odczytu jakiegoś pliku zżera bardzo dużo pamięci RAM. Po drugi nie można za jej pomocą ściągnąć pliku z innego serwera (tzn. nie można jako parametr podać URL zaczynając od http://). Lepiej jest do tego użyć funkcji fopen(), fgets().

Oto cały skrypt download.php z projektu Coyote:

<?
/****************************************************************************
*                           download.php
*                      -------------------------
*       rozpoczety          : 11.03.2003 r.
*       wersja              : $Revision: 1.5 $
*       zmiana              : $Date: 2003/08/12 20:45:52 $
*
*****************************************************************************
 
  Skrypt sluzy do liczania ilosci sciagniec pliku, a po uaktualnieniu w bazie
  danych rozpoczyna sie proces przekierowania na wybrany plik, do sciagniecia
 
****************************************************************************/
 
  include 'functions.php';
 
  // sprawdzaj, czy wraz z URL dostarczony jest paramtr ID
  if (empty($_GET['id']))
  {
      Error('Złe wywołanie programu!', '', 0, NORMAL_ERROR);
  }
 
  $sql = 'SELECT file_name, file_type, file_url, file_size FROM ' . FILES . ' WHERE file_id=' . $_GET['id'];
  if ($db->sql_query($sql))
  {
      /* jezeli zapytanie sie powiodlo, odczytaj zawartosc okreslonego rekordu, w tym jego
         URL, ktory bedzie potrzebny */
      $row = $db->sql_fetch();
 
     // ostatnie zapytanie ma za zadanie zwiekszyc licznik, sciagniec pliku o 1
      $sql = 'UPDATE ' . FILES . ' SET file_downloads=(file_downloads+1) WHERE file_id=' . $_GET['id'];
      $db->sql_query($sql);
 
      $extension = Array(
          'zip' => 'application/x-zip-compressed',
          'exe' => 'application/exe',
          'pdf' => 'application/pdf',
          'pas' => 'text/plain',
          'html' => 'text/html');
 
      // odczytanie rozszerzenia pliku
      preg_match("/\.(.*)$/", $row['file_name'], $matches);
 
      $file = fopen($row['file_url'], 'r');
 
     /* ponizsze instrukcje powoduja wyslanie pliku do przegladarki (innymi slowy - sciagniecie go) */
      header('Pragma: no-cache');
      header('Content-type: ' . $extension[$matches[1]] . '; name="' . $row['file_name'] . '"');
      header('Content-Disposition: attachment; filename="' . $row['file_name'] . '"');
      header('Content-Length: ' . ($row['file_size'] * 1024));
 
      while (!feof($file))
      {
          echo fread($file, 1024);
      }
 
      fclose($file);
 
 
      exit;
 
  }
 
// $Id: download.php,v 1.5 2003/08/12 20:45:52 Adam Exp $
?>


Najpierw z bazy danych następuje pobranie informacji o pliku (nazwa, rozmiar, ścieżka [URL]). Następnie następuje zwiększenie wartości kolumny 'counter', która odpowiada za przechowywanie ilości ściągnięć pliku. Później mozna już otworzyć URL (korzystając z funkcji fopen()). Teraz można już odczytywać plik kawałek po kawału (po 1 kB) i wysyłać do klienta.



Notka

Jak zauważyłeś podczas wysyłania nagłówka należy określic typ MIME</wiki> wysyłanych danych. Ja zadeklarowałem w tym skrypcie tablicę $extension, która zwiera rozszerzenia plików oraz ich typ MIME</wiki>. Istnieje w PHP funkcja mime_content_type() która podaje typ MIME pliku; wystarczy podać w funkcji ścieżkę pliku jako parametr. Jednak, żeby skorzystać z tej funkcji należy skompilować PHP z parametrem: --with-mime-magic. Dodatkowo funkcja mime_content_type() działa tylko na lokalnych plikach, tzn. dostępnych w obrębie jednego serwera. Jeżeli chcesz pobrać typ MIME pliku, który znajduje się na innym serwerze, niestety mime_content_type() tutaj się nie przyda.

2 komentarze

arturo13 2008-04-01 20:23

Może i prościej ale ta funkcja daje dostęp do plików które nie są dostępne przez www.

Crop 2008-01-19 22:03

A  nie  prościej  zrobić  PHP+MySQL
Podawać  userowi  link  do  <font class="adtext" onmouseover='fo_emituj_reklame(8)' onmouseout='fo_ukryj_reklame()'>pliku</font>  np.
http://mojastrona.phpw/download.php?fid=1111
I  wtedy:

<?php
$sql  =  mysql_fetch_array(mysql_query('select  *  from  download  where  id="'.  $_GET['fid']  .'"'));
$pobran  =  $sql[pobran]+1;
mysql_query('update  download  set  pobran="'.  $pobran  .'"  where  id="'.  $_GET['fid']  .'"');
header("Location:  ".  $sql[plik]);
?>