Bonjour à vous ^_^ ,
Le contexte
Je suis actuellement en stage et suis chargé de l'évolution de l'intranet de l'entreprise. Je reprends donc du code existant et il existe des choses assez mal foutu dont je ne peux pas changer car mon but n'est pas de faire une refonte totale du site mais d'ajouter quelques fonctionnalités, effectuer quelques correctifs, avant une refonte totale du site qui viendra plus tard. Il y a une partie du site qui permet d'écrire des documents avec ckeditor et il est possible de créer des liens avec. Les utilisateurs rencontraient un problème, quand ils avaient besoin de mettre un fichier sur le serveur et de faire pointer le lien dessus, ils étaient obligés de passer par Filezilla, ce qui était évidemment pas très pratique. J'ai donc ajouté une interface qui permet d'upload des fichiers sur le serveur, je récupère même le nom des dossiers présents pour qu'ils puissent choisir s'ils veulent ranger le fichier dans un dossier existant ou de créer un nouveau dossier.
Comme c'était le premier formulaire d'upload de fichier, j'ai dû toucher à la configuration du serveur pour ne pas avoir les soucis de base, j'ai donc modifié le "post_max_size" que j'ai mis à 128M, "upload_max_filesize" à 128M et dans le module "mod_fcgid" j'ai modifié la ligne "MaxRequestLen" que j'ai mis à "134217728". Jusque là, je n'ai pas de problème pour upload des fichiers et même si c'est des gros PDF il n'y a pas de soucis.
Le problème
Il faut savoir que l'entreprise utilisent plusieurs serveurs pour stocker des fichiers afin que tout le monde puisse y avoir accès. Le problème survient quand on essaye d'upload un fichier qui vient d'un des réseaux.
En local, j'utilise Wamp, le fichier s'upload bien sur mon serveur mais, cela prend un temps monstrueux pour s'upload, ça prend 2min pour upload un fichier de 9mo (et j'ai visiblement un problème d'affichage de photo quand je prévisualise la discussion, j'espère que ça partira une fois en ligne et si ça se corrige pas... je sais pas ^^''') :
Il se trouve que tout ce qui est réseau, j'ai des difficultés, c'est ma faiblesse, j'ai du mal, désolé par avance si je dis des trucs très bête x) je serais ravis d'en apprendre plus ! Je me suis dit que ce problème était dû au fait qu'il fallait chercher le fichier sur un autre serveur, alors le temps qu'il fasse l'aller retour ça prend du temps, mais quand même.. si je copie-colle le fichier de l'emplacement réseau sur mon pc ça prend quelques secondes, il n'y a pas de latence, le serveur de fichier a l'air d'aller bien. Alors je n'explique pas vraiment pourquoi sélectionner un fichier sur un réseau multiplie par X le temps d'upload.
Ensuite, sur le serveur, le comportement est légèrement différent :
- Si le fichier fait moins de 800ko, il est upload sur le serveur sans aucun problème
- Si le fichier fait moins de 4mo à peu près, le serveur renvoie une erreur 500
- Si le fichier fait plus de 4mo, le serveur renvoie rien, j'ai juste chrome qui me dit "Failed to load response data"
Sachant qu'il y a toujours ce temps monstrueux pour upload. Un fichier de moins de 1mo devrait être upload instantanément, ici ça prend plusieurs secondes, mais à la limite d'accord, il faut aller chercher le fichier sur le réseau, le ramener sur l'ordi du client et ensuite l'envoyer sur le serveur, c'est comme ça en tout cas que je le comprends et que je me dis pourquoi pas.
Mais à partir de 800ko à peu près, ça devient plus hasardeux, ou en tout cas je ne comprends pas ce qu'il se passe. Par exemple pour un fichier de 2mo, par moment le traitement prend 15s et par moment 37s, et je ne sais pas comment l'expliquer.
Je donnerai des précisions techniques plus bas, mais en gros, pour que l'utilisateur puisse écrire son document, créer des liens et upload des fichiers au fur et à mesure, j'ai mis en place une requête ajax pour pas que la page se recharge. Comme le traitement se faisait dynamiquement sur une autre page, c'était pas super pratique pour voir ce qu'il se passait, je devais aller à chaque fois dans "Network", si j'oubliais de faire F12 ça enregistrait pas la requête alors je devais recharger la page et refaire la manip.. à la longue c'était chiant. J'ai voulu savoir ce qu'il se produisait avec "un vrai formulaire", surtout que la redirection des formulaires renvoient sur la même page de la page où ils sont et donc pour debug je trouve ça plus facile même si c'est affreusement moche.
Bref.. je sélectionne toujours le fichier de 2mo sur le même réseau, j'envoie le formulaire, ça prend 30s cette fois-ci et ça m'affiche bien l'erreur 500. Et si je recharge la page, c'était comme s'il avait réussi à upload 75% du fichier, et en rechargeant la page, ça reprenait l'upload et ça finissait par bien l'envoyer sur le serveur.
Et si je sélectionne un plus gros fichier, genre 4mo, ça va comme d'habitude mettre un certain temps, puis m'afficher :
, et si je recharge une nouvelle fois la page, cela va m'afficher une erreur 500, et si je recharge encore une fois la page, le fichier fini par bien être envoyé sur le serveur.Ce site est inacessible. Il se peut que la page Web à l'adresse [..] soit temporairement inaccessible ou qu'elle ait été déplacée de façon permanente à une autre adresse Web. ERR_CONNECTION_ABORTED
Alors, peut être que c'est normal, qu'il y a une sécurité qui permet de reprendre des upload, mais personnellement ça me paraît très étrange comme comportement. D'autant plus que je n'ai trouvé personne avec un problème similaire.
Au vue de la latence, du fait qu'on peut reprendre l'upload après que le serveur nous envoie une erreur, je me suis dit que ça pourrait venir de la configuration de php. J'ai vu qu'il avait un "max_execution_time" et un "max_input_time", qui correspond "au temps maximal d'exécution d'un script" et "spécifie la durée maximale pour analyser les données d'entrée". Je me suis dit que le temps de traitement devait prendre trop temps et que c'était pour ça que ça me retournait une erreur à un moment. MAIS, le truc c'est qu'en locale avec Wamp, je n'ai pas ces soucis. J'ai même modifié ces attributs pour les mettre à 20, donc 20s, et même si l'upload prend 2min, cela me renvoie aucune erreur et upload bien le fichier. J'ai quand même décidé de tester sur le serveur de passer de 30 à 120 mais ça change pas que même les fichiers de 1mo mettent une trentaine de secondes (ou moins) avant de m'envoyer l'erreur. J'en ai conclu que le problème ne venait pas de là.
Je suis allé très vite checker le log des erreurs pour voir si je pouvais en apprendre davantage et j'avais cette erreur quand le problème survenait :
J'ai trouvé des personnes qui avaient cette même erreur, mais le contexte était très différent. C'était des personnes qui n'arrivaient pas à upload des fichiers en local vers le serveur. On leur conseillait d'augmenter la taille max d'upload, de POST, le "max_execution_time", le "max_input_time" et d'ajouter des lignes dans le fichier "mod_fcgid.conf" comme "FcgidConnectTimeout 20". Généralement ça résolvait le problème et bien évidemment ça n'a pas fonctionné pour moi.(70007)The timeout specified has expired: mod_fcgid: can't get data from http client, referer
D'ailleurs, en fonction des forums, les personnes conseillaient d'ajouter plus ou de moins certaines lignes dans ce fichier alors que le problème était le même de ce que j'avais compris. Aucune modifications de ce fichier n'a fait évolué le problème.
La solution provisoire
Pour l'instant il faut copier-coller les fichiers du réseau sur sa machine, les upload via mon interface et les supprimer en locale. C'est évidemment pas très pratique, et qu'en plus il y a certaines recommandations pour les utilisateurs, comme de ne pas sortir les fichiers du serveur. Pour le moment c'est acceptable car la solution n'est qu'en phase de test avec la responsable qui a demandé cette interface, mais à terme, j'aimerais trouver une solution.
Si vous avez des idées pour résoudre mon problème ou le contourner, je suis tout ouïe !
Le point technique
J'utilise en locale :
WampServer 3.2.0
Apache 2.4.41
PHP 7.4.0
Sur le serveur :
Apache 2.2.22
PHP 5.6.31
Je suis malheureusement obligé de rester sur Boostrap 3 TuT, résultat de l'interace :
Le code de l'interface :
La requête ajax :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86 <?php // Construction du select pour afficher le nom de tous les dossiers présents sur le serveur $chemin = "img/smq/"; $tableauNomDossier = []; if ($dossier = opendir($chemin)) { $optionsUpload = "<optgroup label='Dossier existant'>"; while ($fichier = readdir($dossier)) { if($fichier != "." && $fichier != ".." && filetype($chemin.$fichier) == "dir") $tableauNomDossier[] = $fichier; } sort($tableauNomDossier); foreach($tableauNomDossier as $nomDossier) $optionsUpload .= " <option value='$nomDossier'>$nomDossier</option>"; $optionsUpload .= " </optgroup>\n"; closedir($dossier); } ?> <div id="alertErrorUpload" class="alert alert-danger" role="alert" hidden>Message défaut</div> <nav class='navbar navbar-default'> <div class='container-fluid'> <!-- Brand and toggle get grouped for better mobile display --> <div class='navbar-header'> <button type='button' class='navbar-toggle collapsed' data-toggle='collapse' data-target='#navbar-upload' aria-expanded='false'> <span class='sr-only'>Toggle navigation</span> <span class='icon-bar'></span> <span class='icon-bar'></span> <span class='icon-bar'></span> </button> <span class='navbar-brand' href='#'>Upload</span> </div> <!-- Collect the nav links, forms, and other content for toggling --> <div class='collapse navbar-collapse' id='navbar-upload'> <div class='navbar-form'> <div class='form-group'> <select id="selectUpload" name="selectUpload" class="form-control"> <optgroup label="Nouveau dossier"> <option value="optionCreerDossier">Créer un nouveau dossier</option> </optgroup> <?php echo $optionsUpload; ?> </select> <input id="nomCreerDossier" name="nomCreerDossier" type="text" class="form-control" placeholder="Nom du nouveau dossier"> </div> <div class='form-group'> <input type="hidden" name="MAX_FILE_SIZE" value="100000000"/> <input type="file" name="fichier" id="fichier"> </div> <div class='form-group'> <span id="boutonCreerDossier" class='enabled btn btn-grey' data-loading-text="Loading...">Envoyer</span> </div> </div> </div><!-- /.navbar-collapse --> </div><!-- /.container-fluid --> </nav> <p style="margin-bottom:20px"> L'upload peut prendre un <strong>certain temps</strong>. Une fois que vous avez cliqué sur "Envoyer", <strong>veuillez attendre</strong> le résultat ci-dessous. </p> <nav class='navbar navbar-default' style='margin-bottom:0px'> <div class='container-fluid'> <!-- Brand and toggle get grouped for better mobile display --> <div class='navbar-header'> <button type='button' class='navbar-toggle collapsed' data-toggle='collapse' data-target='#navbar-resultat' aria-expanded='false'> <span class='sr-only'>Toggle navigation</span> <span class='icon-bar'></span> <span class='icon-bar'></span> <span class='icon-bar'></span> </button> <span class='navbar-brand' href='#'>Résultat</span> </div> <!-- Collect the nav links, forms, and other content for toggling --> <div class='collapse navbar-collapse' id='navbar-resultat'> <div class='navbar-form form-inline'> <div class='form-group'> <span id='isoLienFichierUpload' class="form-control" rows="1" readonly>lien/de/votre/futur/fichier</span> <span id="boutonCopie" class="btn btn-grey form-control"data-clipboard-target="#isoLienFichierUpload" title="Copie le lien" data-toggle="tooltip" data-placement="bottom"><i class="far fa-copy fa-lg"></i></span> </div> </div> </div><!-- /.navbar-collapse --> </div><!-- /.container-fluid --> </nav>
Le traitement PHP :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52 var uploadData = new FormData(); var fichier = $('#fichier')[0].files[0]; var selectUpload = $("#selectUpload").val(); var nomCreerDossier = $("#nomCreerDossier").val(); var tailleFichier = $("#fichier")[0].files[0]["size"]; uploadData.append('fichier',fichier); uploadData.append('selectUpload',selectUpload); uploadData.append('nomCreerDossier',nomCreerDossier); uploadData.append('tailleFichier', tailleFichier); // Supprime la valeur dans le champ fichier pour empêcher l'utilisateur de spammer le bouton d'envoi et met le bouton en mode chargement $("#fichier").val(""); var $btnEnvoyer = $(this).button('loading'); $.ajax({ url: 'isouploadfichier.php', type: 'post', data: uploadData, dataType: "json", contentType: false, processData: false, success: function(reponse){ if(reponse["cheminUpload"] != null) $("#isoLienFichierUpload").text(reponse["cheminUpload"]); else setMessageErrorAlert(reponse["messageErreur"]); if(reponse["nomDossierAjouter"] != null){ var nomDossierAjouter = reponse["nomDossierAjouter"]; $("#selectUpload optgroup[label='Nouveau dossier']").append($('<option>', { value: nomDossierAjouter, text: nomDossierAjouter })); } if(reponse["dossierASelectionner"] != null){ var nomDossierAjouter = reponse["dossierASelectionner"]; $("#selectUpload").val(nomDossierAjouter); $("#nomCreerDossier").hide(); } }, error: function(){ setMessageErrorAlert("Le fichier n'a pas pu être upload"); } }).always(function() { $btnEnvoyer.button('reset') }); }
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67 <?php /* Fichier qui permet d'upload des fichiers depuis la page iso */ include('controleacces.php'); include('mysql.php'); // "cheminUpload" : permet de connaître le chemin où est stocké le fichier upload par l'utilisateur sur le serveur // "nomDossierAjouter" : s'il est est différent de null, permet de savoir quel nom de dossier il faut ajouter dans le select sur la page "iso.php". Comme on ne recharge pas la page, il faut l'ajouter de façon dynamique en js // "dossierASelectionner" : il est possible que l'utilisateur entre un nom de dossier qui existe déjà, alors on stocke le fichier dans le dossier existant et il faut aussi changer l'option qui est séléctionné dans le select de façon dynamique // "messageErreur" : permet de faire passer des messages d'erreur qu'on gère ensuite en js $reponseAjax =["cheminUpload" => null, "nomDossierAjouter" => null, "dossierASelectionner" => null, "messageErreur" => null]; // Récupère la taille du fichier en byte et convertit la taille max d'upload du serveur en byte $tailleFichier = $_POST['tailleFichier']; $tailleUploadMax = substr(ini_get('upload_max_filesize'),0,-1); // Transforme "128M" => "128" $taillePostMax = substr(ini_get('post_max_size'),0,-1); // Transforme "128M" => "128" $tailleUploadMax = $tailleUploadMax * pow(1024,2); $reponseAjax["messageErreur"] .= "Debut//"; // Si le fichier est trop lourd on informe l'utilisateur if($tailleFichier > $tailleUploadMax || $tailleFichier > $tailleUploadMax) { $reponseAjax["messageErreur"] = "Le fichier est trop volumineux, il n'a pas pu être upload"; } // Sinon déplace le fichier dans le répertoire du serveur else if (!empty($_FILES['fichier']['name'])) { $nomFichier = $_FILES['fichier']['name']; $cheminUpload = "img/smq/"; // Créer un nouveau dossier si besoin et construit le chemin du fichier if($_POST["selectUpload"] == "optionCreerDossier") { $nomDossier = mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $_POST['nomCreerDossier']); if(!file_exists($cheminUpload.$nomDossier)){ mkdir($cheminUpload.$nomDossier,0755); $reponseAjax["nomDossierAjouter"] = $nomDossier; } $cheminUpload .= $nomDossier."/"; $reponseAjax["dossierASelectionner"] = $nomDossier; } else { $cheminUpload .= $_POST["selectUpload"]."/"; } $cheminUploadTemp = $cheminUpload.$nomFichier; // Récupére les informations du fichier upload $fichierInfo = pathinfo($_FILES["fichier"]["name"]); // Tant que le fichier upload porte le même nom d'un fichier existant, modifie son nom par "nom_X.extension" et met à jour le chemin for($i = 1; file_exists($cheminUploadTemp); $i++){ $_FILES["fichier"]["name"] = $fichierInfo["filename"]."_".$i.".".$fichierInfo["extension"]; $cheminUploadTemp = $cheminUpload.$_FILES['fichier']['name']; } if(move_uploaded_file($_FILES['fichier']['tmp_name'],$cheminUploadTemp)) $reponseAjax["cheminUpload"] = $cheminUploadTemp; else $reponseAjax["messageErreur"] = "Une erreur est survenue lors de l'upload du fichier"; } else { $reponseAjax["messageErreur"] = "Aucun fichier a été sélectionné ou le fichier est trop volumineux"; } $reponseAjax["messageErreur"] .= "//fin"; echo json_encode($reponseAjax);
J'espère qu'il manque pas d'information. J'espère que c'est pas un problème commun, mais j'ai passé plusieurs heures dessus sans trouver de solution, sans même comprendre et cibler d'où venait vraiment le problème en fait. J'ai pas trouvé de cas similaire aussi. En tout cas je suis très intrigué et j'aimerais vraiment comprendre le pourquoi du comment.
Merci d'avoir pris le temps de me lire et je remercie par avance toutes les personnes qui se pencheront sur mon problème !
Partager