J'ai un problème très étrange lors du téléchargement de fichiers volumineux de plus de 6 Go. Mon processus fonctionne comme ceci:
Ma configuration PHP (HHVM) et NGINX ont toutes deux leur configuration définie pour autoriser jusqu'à 16 Go de fichier, mon fichier de test ne fait que 8 Go.
Voici la partie étrange, l'ajax va [~ # ~] toujours [~ # ~] expirer. Mais le fichier est correctement téléchargé, il est copié vers l'emplacement tmp, l'emplacement stocké dans le db, s3, etc. Mais le AJAX fonctionne pendant une heure, même APRÈS que toute l'exécution soit terminée (ce qui prend 10-15 minutes) et ne se termine que lorsque le temps est écoulé.
Qu'est-ce qui peut empêcher le serveur d'envoyer une réponse uniquement pour les fichiers volumineux?
Les journaux d'erreurs côté serveur sont également vides.
Un téléchargement de fichiers volumineux est une opération coûteuse et sujette aux erreurs. Nginx et le backend devraient avoir un timeout correct configuré pour gérer le disque lent IO si cela se produit. En théorie, il est simple de gérer le téléchargement de fichiers en utilisant le codage multipart/form-data RFC 1867.
Selon developer.mozilla.org dans un corps multipart/form-data, l'en-tête général HTTP Content-Disposition est un en-tête qui peut être utilisé sur la sous-partie d'un corps multipart pour donner des informations sur le champ il s'applique à. La sous-partie est délimitée par la limite définie dans l'en-tête Content-Type. Utilisé sur le corps lui-même, Content-Disposition n'a aucun effet.
Voyons ce qui se passe lors du téléchargement du fichier:
1) le client envoie une requête HTTP avec le contenu du fichier au serveur Web
2) le serveur Web accepte la demande et lance le transfert de données (ou renvoie l'erreur 413 si la taille du fichier dépasse la limite)
3) le serveur Web commence à remplir les tampons (dépend de la taille du fichier et des tampons)
4) Le serveur Web envoie le contenu du fichier via le fichier/socket réseau au backend
5) le backend authentifie la demande initiale
6) le backend lit le fichier et coupe les en-têtes (Content-Disposition, Content-Type)
7) Les sauvegardes dorsales ont généré le fichier sur le disque
8) toute procédure de suivi comme les modifications de la base de données
Lors du téléchargement de fichiers volumineux, plusieurs problèmes se produisent:
Commençons par Nginx configuré avec un nouvel emplacement http: // backend/upload pour recevoir le téléchargement de fichiers volumineux, interaction back-end est minimisé (Content-Legth: 0), le fichier est stocké uniquement sur le disque. À l'aide de tampons, Nginx vide le fichier sur le disque (un fichier stocké dans le répertoire temporaire avec un nom aléatoire, il ne peut pas être modifié) suivi de POST demande de backend à l'emplacement http: // backend/fichier avec le nom du fichier dans X-File-Name entête.
Pour conserver des informations supplémentaires, vous pouvez utiliser des en-têtes avec la demande initiale POST. Par exemple, avoir X-Original-File-Name les en-têtes des demandes initiales vous aident à faire correspondre le fichier et à stocker les informations de mappage nécessaires dans la base de données.
Voyons comment y arriver:
1) configurer Nginx pour vider le contenu du corps HTTP dans un fichier et le conserver stocké client_body_in_file_only on;
2) créer un nouveau point de terminaison backend http: // backend/fichier pour gérer le mappage entre le nom du fichier temporaire et l'en-tête X-File-Name
4) instrument AJAX requête avec en-tête X-File-Name Nginx utilisera pour envoyer une demande de post-téléchargement avec
Configuration:
location /upload {
client_body_temp_path /tmp/;
client_body_in_file_only on;
client_body_buffer_size 1M;
client_max_body_size 7G;
proxy_pass_request_headers on;
proxy_set_header X-File-Name $request_body_file;
proxy_set_body off;
proxy_redirect off;
proxy_pass http://backend/file;
}
L'option de configuration Nginx client_body_in_file_only est incompatible avec le téléchargement de données en plusieurs parties, mais vous pouvez l'utiliser avec AJAX i.e. XMLHttpRequest2 (données binaires).
Si vous avez besoin d'une authentification principale, la seule façon de gérer est d'utiliser auth_request , par exemple:
location = /upload {
auth_request /upload/authenticate;
...
}
location = /upload/authenticate {
internal;
proxy_set_body off;
proxy_pass http://backend;
}
La logique d'authentification préalable au téléchargement protège des demandes non authentifiées quelle que soit la taille initiale POST Content-Length).