Comment forcer le téléchargement de gros fichiers sans utiliser trop de mémoire?
Je suis en train de servir de grandes des fichiers zip pour les utilisateurs. Quand il y a 2 connexions simultanées, le serveur est à court de mémoire vive (RAM). J'ai augmenté la quantité de mémoire à partir de 300 MO à 4 go (Dreamhost VPS) et puis il a bien fonctionné.
J'ai besoin pour permettre à beaucoup plus de 2 connexions simultanées. La réelle 4 GO permettrait à quelque chose comme 20 connexions simultanées (trop mal).
Bien, le code actuel, je suis à l'aide, a besoin du double de mémoire, puis la taille réelle du fichier. C'est trop mauvais. Je veux quelque chose comme "streaming" le fichier à l'utilisateur. Donc, je n'accorderai pas plus que le morceau d'être transmis aux utilisateurs.
Le code suivant est celui que j'utilise dans CodeIgniter (framework PHP):
ini_set('memory_limit', '300M'); //it was the maximum amount of memory from my server
set_time_limit(0); //to avoid the connection being terminated by the server when serving bad connection downloads
force_download("download.zip", file_get_contents("../downloads/big_file_80M.zip"));exit;
La force_download fonction est la suivante (CodeIgniter par défaut de la fonction d'assistance):
function force_download($filename = '', $data = '')
{
if ($filename == '' OR $data == '')
{
return FALSE;
}
//Try to determine if the filename includes a file extension.
//We need it in order to set the MIME type
if (FALSE === strpos($filename, '.'))
{
return FALSE;
}
//Grab the file extension
$x = explode('.', $filename);
$extension = end($x);
//Load the mime types
@include(APPPATH.'config/mimes'.EXT);
//Set a default mime if we can't find it
if ( ! isset($mimes[$extension]))
{
$mime = 'application/octet-stream';
}
else
{
$mime = (is_array($mimes[$extension])) ? $mimes[$extension][0] : $mimes[$extension];
}
//Generate the server headers
if (strpos($_SERVER['HTTP_USER_AGENT'], "MSIE") !== FALSE)
{
header('Content-Type: "'.$mime.'"');
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: ".strlen($data));
}
else
{
header('Content-Type: "'.$mime.'"');
header('Content-Disposition: attachment; filename="'.$filename.'"');
header("Content-Transfer-Encoding: binary");
header('Expires: 0');
header('Pragma: no-cache');
header("Content-Length: ".strlen($data));
}
exit($data);
}
J'ai essayé quelques morceau codes que j'ai trouvé dans Google, mais le fichier était toujours livré endommagé. Probablement à cause du mauvais code.
Quelqu'un pourrait-il m'aider?
Location
?Me semble que vous êtes beaucoup mieux simplement en offrant aux utilisateurs un lien direct vers les fichiers...
J'ai oublié de dire que les fichiers sont dans un dossier n'est pas accessible via le web. C'est pour des raisons de sécurité. Je viens de servir le fichier si l'utilisateur passe à un processus d'authentification. Je vais essayer les suggestions ci-dessous et nous reviendrons à voter pour la meilleure réponse.
Merci pour le texte de révision, @p.campbell je pense que j'étais trop fatigué la nuit dernière... 🙂
OriginalL'auteur Leandro Alves | 2011-06-01
Vous devez vous connecter pour publier un commentaire.
Il y a quelques idées de plus en ce fil. Je ne sais pas si le readfile() la méthode permettra d'économiser de la mémoire, mais il semble prometteur.
readfile
ne économiser de la mémoire, parce que chaque morceau du fichier il lit obtient en sortie directement dans le navigateur sans le stocker dans une variable (et donc sans l'aide de la mémoire supplémentaire, juste le nécessaire pour le fichier de morceau).Qui a très bien fonctionné. J'ai juste ajouté un peu plus les en-têtes, de sorte que l'iPhone ne fait pas d'alerte d'une erreur de téléchargement.
header('Content-Disposition: attachment; filename="download.zip"'); 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($filename)); readfile($filename);exit;
Content que ça a fonctionné!
J'ai d'abord pensé que
readfile
oufpassthru
marcherait aussi bien, mais juste couru dans un problème d'aujourd'hui, où il semble quereadfile
fait encore lit tout le fichier en mémoire. Peut-être qui a été modifié dans une version plus récente de PHP (j'ai été en utilisant 5.2).OriginalL'auteur King Skippus
Vous envoyez le contenu ($data) de ce fichier via PHP?
Si si, chaque processus Apache de la manipulation de ce sera à la fin croissant de la taille de ce fichier, les données sont mises en cache.
Votre SEULE solution est de ne pas envoyer le contenu du fichier/données via PHP et il suffit de rediriger l'utilisateur vers une URL de téléchargement sur le système de fichiers.
Utiliser un générés et unique lien symbolique, ou un endroit caché.
OriginalL'auteur rightstuff
Vous ne pouvez pas utiliser $de données avec l'ensemble du fichier de données à l'intérieur. Essayez de passer à cette fonction n'est pas le contenu d'un fichier seulement son chemin. Ensuite l'envoyer tous les en-têtes une fois et après que de lire une partie de ce fichier à l'aide de fread(), l'écho de ce morceau, appeler la méthode flush() et répéter. Si un autre en-tête sera envoyer dans l'intervalle, puis, enfin, le transfert sera endommagé.
readfile
n'readfile
lit tout le fichier à la fois, dans ma proposition, j'ai utiliséfread
(peut être utiliséfgets
) parce que si le fichier sera lu dans i'e 1 MO morceaux alors que la mémoire peut être libéré lors de la partie suivante sera affecté à la même variable.désolé, mais non,
readfile
lit le fichier en blocs de 8 k. En fait, j'ai fouillé dans le code php, car qui m'a intrigué, et confirmé. readfile source, ligne 1383,php_stream_passthru
y est DÉFINI à_php_stream_passthru
(source, la ligne 453) et que, plus tard de la fonction ce fichier, ligne 1314Dans ce cas, il doit être décrit dans la documentation de php.
OriginalL'auteur Arek Jablonski
Lien symbolique le gros fichier à la racine du document (en supposant que ses pas autorisés d'un seul fichier), puis laissez Apache gérer. (De cette façon, vous pouvez accepter les plages d'octet)
OriginalL'auteur Petah
Ajouter votre
ini_set
avantSESSION_START();
OriginalL'auteur Emile Swarts