Comment forcer le téléchargement de gros fichiers sans utiliser trop de mémoire?

J’essaie de servir de gros fichiers zip aux utilisateurs. Lorsqu’il y a 2 connexions simultanées, le serveur manque de mémoire (RAM). J’ai augmenté la quantité de mémoire de 300 Mo à 4 Go (Dreamhost VPS), puis ça a bien fonctionné.

Je dois autoriser plus de 2 connexions simultanées. Le réel 4 Go permettrait quelque chose comme 20 connexions simultanées (tant pis).

Eh bien, le code que j’utilise actuellement nécessite le double de la mémoire puis la taille réelle du fichier. C’est dommage. Je veux quelque chose comme “streaming” le fichier à l’utilisateur. Donc, je n’atsortingbuerais pas plus que le morceau servi 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 fonction force_download est la suivante (fonction d’assistance par défaut CodeIgniter):

 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é certains codes basés sur des morceaux que j’ai trouvés dans Google, mais le fichier a toujours été livré corrompu. Probablement à cause du mauvais code.

Quelqu’un pourrait-il m’aider?

Il y a quelques idées dans ce fil . Je ne sais pas si la méthode readfile () permettra d’économiser de la mémoire, mais cela semble prometteur.

Vous envoyez le contenu ($ data) de ce fichier via PHP?

Si tel est le cas, chaque processus Apache traitant cela finira par atteindre la taille de ce fichier, car ces données seront mises en cache.

Votre SEULE solution est de ne pas envoyer le contenu / données du fichier via PHP et simplement redirect l’utilisateur vers une URL de téléchargement sur le système de fichiers.

Utilisez un lien symbolique généré et unique, ou un emplacement masqué.

Vous ne pouvez pas utiliser $ data avec des données de fichiers entières à l’intérieur. Essayez de passer à cette fonction pas le contenu du fichier seulement son chemin. Ensuite, envoyez tous les en-têtes une fois et après avoir lu une partie de ce fichier en utilisant fread (), faites écho à ce morceau, appelez flush () et répétez. Si un autre en-tête est envoyé entre-temps, le transfert sera finalement corrompu.

Symlink le gros fichier à la racine de votre document (en supposant que ce n’est pas un fichier uniquement autorisé), puis laissez Apache le gérer. (De cette façon, vous pouvez également accepter des plages d’octets)

Ajoutez votre ini_set avant SESSION_START();