En utilisant PHP, j'essaie de servir des fichiers volumineux (jusqu'à éventuellement 200 Mo) qui ne sont pas dans un répertoire accessible sur le Web en raison de problèmes d'autorisation. Actuellement, j'utilise un appel readfile()
avec quelques en-têtes pour servir le fichier, mais il semble que PHP le charge en mémoire avant de l'envoyer. J'ai l'intention de déployer sur un serveur d'hébergement partagé, qui ne me permettra pas d'utiliser beaucoup de mémoire ou d'ajouter mes propres modules Apache tels que X-Sendfile.
Je ne peux pas laisser mes fichiers dans un répertoire accessible sur le Web pour des raisons de sécurité. Quelqu'un connaît-il une méthode moins gourmande en mémoire que je pourrais déployer sur un serveur d'hébergement partagé?
ÉDITER:
if(/* My authorization here */) {
$path = "/uploads/";
$name = $row[0]; //This is a MySQL reference with the filename
$fullname = $path . $name; //Create filename
$fd = fopen($fullname, "rb");
if ($fd) {
$fsize = filesize($fullname);
$path_parts = pathinfo($fullname);
$ext = strtolower($path_parts["extension"]);
switch ($ext) {
case "pdf":
header("Content-type: application/pdf");
break;
case "Zip":
header("Content-type: application/Zip");
break;
default:
header("Content-type: application/octet-stream");
break;
}
header("Content-Disposition: attachment; filename=\"".$path_parts["basename"]."\"");
header("Content-length: $fsize");
header("Cache-control: private"); //use this to open files directly
while(!feof($fd)) {
$buffer = fread($fd, 1*(1024*1024));
echo $buffer;
ob_flush();
flush(); //These two flush commands seem to have helped with performance
}
}
else {
echo "Error opening file";
}
fclose($fd);
Si vous utilisez fopen
et fread
au lieu de readfile
, cela devrait résoudre votre problème.
Il y a ne solution dans la documentation readfile
de PHP montrant comment utiliser fread
pour faire ce que vous voulez.
Pour télécharger des fichiers volumineux à partir du serveur, j'ai modifié les paramètres ci-dessous dans le fichier php.ini:
Upload_max_filesize - 1500 M
Max_input_time - 1000
Memory_limit - 640M
Max_execution_time - 1800
Post_max_size - 2000 M
Maintenant, je peux télécharger et télécharger 175 Mo de vidéo sur le serveur. Depuis, j'ai le serveur dédié. Donc, faire ces changements était facile.
Vous trouverez ci-dessous le script PHP pour télécharger le fichier. Je n'ai apporté aucune modification à cet extrait de code pour les fichiers de grande taille.
// Begin writing headers
ob_clean(); // Clear any previously written headers in the output buffer
if($filetype=='application/Zip')
{
if(ini_get('zlib.output_compression'))
ini_set('zlib.output_compression', 'Off');
$fp = @fopen($filepath, 'rb');
if (strstr($_SERVER['HTTP_USER_AGENT'], "MSIE"))
{
header('Content-Type: "$content_type"');
header('Content-Disposition: attachment; filename="'.$filename.'"');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header("Content-Transfer-Encoding: binary");
header('Pragma: public');
header("Content-Length: ".filesize(trim($filepath)));
}
else
{
header('Content-Type: "$content_type"');
header('Content-Disposition: attachment; filename="'.$filename.'"');
header("Content-Transfer-Encoding: binary");
header('Expires: 0');
header('Pragma: no-cache');
header("Content-Length: ".filesize(trim($filepath)));
}
fpassthru($fp);
fclose($fp);
}
elseif($filetype=='audio'|| $filetype=='video')
{
global $mosConfig_absolute_path,$my;
ob_clean();
header("Pragma: public");
header('Expires: 0');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: pre-check=0, post-check=0, max-age=0');
header("Cache-Control: public");
header("Content-Description: File Transfer");
header("Content-Type: application/force-download");
header("Content-Type: $content_type");
header("Content-Length: ".filesize(trim($filepath)));
header("Content-Disposition: attachment; filename=\"$filename\"");
// Force the download
header("Content-Transfer-Encoding: binary");
@readfile($filepath);
}
else{ // for all other types of files except Zip,audio/video
ob_clean();
header("Pragma: public");
header('Expires: 0');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: pre-check=0, post-check=0, max-age=0');
header("Cache-Control: public");
header("Content-Description: File Transfer");
header("Content-Type: $content_type");
header("Content-Length: ".filesize(trim($filepath)));
header("Content-Disposition: attachment; filename=\"$filename\"");
// Force the download
header("Content-Transfer-Encoding: binary");
@readfile($filepath);
}
exit;
Si vous vous souciez des performances, il existe xsendfile, disponible dans Apache, nginx et lighttpd en tant que module. Vérifiez les commentaires des utilisateurs du document readfile()
.
Il existe également des modules pour ces serveurs Web qui acceptent une URL avec une valeur de hachage supplémentaire qui permet de télécharger le fichier pendant une courte période. Cela peut également être utilisé pour résoudre les problèmes d'autorisation.
Vous pouvez également gérer cela dans le style du nœud gordien - c'est-à-dire éviter complètement le problème. Conservez les fichiers dans un répertoire non accessible, et lorsqu'un téléchargement est lancé, vous pouvez simplement
tempstring = Rand();
symlink('/filestore/filename.extension', '/www/downloads'.tempstring.'-filename.extension');
echo("Your download is available here: <a href='/downloads/'.tempstring.'-filename.extension');
et configurez un cronjob sur unlink()
tout lien de téléchargement de plus de 10 minutes. Pratiquement aucun traitement de vos données n'est requis, aucun massage des en-têtes HTTP, etc.
Il existe même quelques bibliothèques dans ce but.