Comment livrer des fichiers volumineux dans ASP.NET Response?

Je ne cherche pas d’alternative de streaming de contenu de firebase database, en effet je suis à la recherche de la racine du problème, ce fut le fichier courant jusqu’à IIS 6 où nous avons exécuté notre application en mode classique, maintenant nous avons mis à niveau notre IIS à 7 et nous sums l’exécution du pool d’applications en mode pipeline et ce problème a commencé.

J’ai un gestionnaire, où je dois livrer de gros fichiers à la demande du client. Et je suis confronté aux problèmes suivants,

Les fichiers ont une taille moyenne de 4 à 100 Mo, donc considérons le cas du téléchargement de fichiers de 80 Mo.

Mise en mémoire tampon, démarrage lent

Response.BufferOutput = True; 

Cela se traduit par un démarrage très lent des fichiers, car les téléchargements des utilisateurs et même la barre de progression n’apparaissent que pendant quelques secondes, généralement 3 à 20 secondes. IIS lit tout le fichier en premier, détermine la longueur du contenu et commence le transfert . Le fichier est en cours de lecture dans le lecteur vidéo, et il est très lent, mais l’iPad ne télécharge qu’une fraction de fichier en premier, donc il fonctionne rapidement.

Mise en mémoire tampon désactivée, aucune longueur de contenu, démarrage rapide, aucune progression

 Reponse.BufferOutput = False; 

Cela se traduit par un démarrage immédiat, mais le client final (navigateur classique comme Chrome) ne connaît pas Content-Length car IIS ne le sait pas non plus, il n’affiche donc pas la progression, mais indique X KB téléchargée.

Mise en mémoire tampon désactivée, longueur de contenu manuelle, démarrage rapide, progression et violation de protocole

 Response.BufferOutput = False; Response.AddHeader("Content-Length", file.Length); 

Cela se traduit par un téléchargement immédiat des fichiers correct dans Chrome, etc. Cependant, dans certains cas, le gestionnaire IIS génère une erreur «Connexion client distante» (ce qui est très fréquent) et d’autres résultats WebClient entraînent une violation du protocole. Cela se produit 5 à 10% de toutes les demandes, pas toutes les demandes.

Je suppose que ce qui se passe est que IIS n’envoie rien appelé 100 continue lorsque nous ne faisons pas de mise en mémoire tampon et que le client peut se déconnecter sans attendre aucune sortie. Cependant, la lecture des fichiers à partir des sources peut prendre plus de temps, mais du côté du client, j’ai dépassé le délai d’attente, mais je semble avoir dépassé le délai d’attente d’IIS et je n’ai aucun contrôle.

Est-ce que je peux forcer Response à envoyer 100 continuer et à ne laisser personne fermer la connexion?

METTRE À JOUR

J’ai trouvé les en-têtes suivants dans Firefox / Chrome, rien ne semble inhabituel ici pour la violation de protocole ou l’en-tête incorrect.

 Access-Control-Allow-Headers:* Access-Control-Allow-Methods:POST, GET, OPTIONS Access-Control-Allow-Origin:* Access-Control-Max-Age:1728000 Cache-Control:private Content-Disposition:attachment; filename="24.jpg" Content-Length:22355 Content-Type:image/pjpeg Date:Wed, 07 Mar 2012 13:40:26 GMT Server:Microsoft-IIS/7.5 X-AspNet-Version:4.0.30319 X-Powered-By:ASP.NET 

MISE À JOUR 2

Turning Recycling n’offrait toujours pas beaucoup, mais j’ai augmenté mon MaxWorkerProcess à 8 et je reçois maintenant moins d’erreurs.

Mais en moyenne, sur 200 demandes en une seconde, 2 à 10 demandes échouent., Et cela se produit presque toutes les secondes.

MISE À JOUR 3

Poursuivant 5% des requêtes échouant avec “Le serveur a commis une violation de protocole. Section = ResponseStatusLine”, j’ai un autre programme qui télécharge le contenu du serveur Web qui utilise WebClient, et qui donne cette erreur 4 à 5 fois par seconde. 5% des demandes échouent. Y a-t-il de toute façon une trace de l’échec WebClient?

Problèmes redéfinis

Fichier de zéro octet reçu

IIS ferme la connexion pour une raison quelconque, côté client dans WebConfig, je reçois 0 octet pour le fichier qui n’est pas zéro octet.

Cela a été mon erreur, et sa résolution, étant donné que nous utilisons Entity Framework, que la lecture était sale (les lignes non validées) car read n’était pas dans la scope de la transaction, la place dans la scope de la transaction a résolu ce problème.

Exception de violation de protocole soulevée

WebClient lance WebException en disant “Le serveur a commis une violation de protocole. Section = ResponseStatusLine.

Je sais que je peux activer l’parsing des en-têtes non sécurisés, mais ce n’est pas le cas. Lorsque mon gestionnaire HTTP envoie des en-têtes appropriés, je ne sais pas pourquoi IIS envoie des données supplémentaires. % de fois.

MISE À JOUR 4

Erreur sc-win32 64 trouvé et j’ai lu quelque part que WebLimits pour MinBytesPerSecond doit être changé de 240 à 0, toujours tout est pareil. Cependant, j’ai remarqué que chaque fois qu’IIS enregistre une erreur sc-win32, IIS enregistre le statut HTTP sous la forme 200, mais une erreur s’est produite. Maintenant, je ne peux pas activer la journalisation de trace échouée pour 200 car cela entraînera des fichiers massifs.

Les deux problèmes ci-dessus ont été résolus en augmentant MinBytesPerSecond et en désactivant les sessions, j’ai ajouté une réponse détaillée résumant chaque point.

Bien que la manière correcte de dissortingbuer les gros fichiers dans IIS soit l’option suivante,

  1. Définissez MinBytesPerSecond sur Zero dans WebLimits (Cela aidera certainement à améliorer les performances, car IIS choisit de fermer les clients détenant des connexions KeepAlive avec des transferts de taille inférieure)
  2. Allouer plus de processus de travail au pool d’applications, j’ai défini à 8, maintenant cela devrait être fait que si votre serveur dissortingbue des fichiers plus volumineux. Cela entraînera certainement un ralentissement des autres sites, mais cela garantira de meilleures livraisons. Nous avons fixé à 8, car ce serveur ne possède qu’un seul site Web et ne fournit que des fichiers volumineux.
  3. Désactiver le recyclage des pools d’applications
  4. Désactiver les sessions
  5. Laisser la mise en mémoire tampon
  6. Avant chacune des étapes suivantes, vérifiez si Response.IsClientConnected est vrai, sinon abandonnez et n’envoyez rien.
  7. Définir Content-Length avant d’envoyer le fichier
  8. Rincer la réponse
  9. Ecrire dans le stream de sortie et vider à intervalles réguliers

Lorsque vous avez défini la longueur du contenu avec bufferOutput sur false, la raison possible de l’échec est que IIS essaie de compresser le fichier que vous envoyez et que, par définition, le service IIS Content-Length ne peut plus être compressé. les erreurs commencent (*).

keep the BufferOutput to false, and second disable the gzip from iis for the files you send donc keep the BufferOutput to false, and second disable the gzip from iis for the files you send – ou désactivez le gzip iis pour tous les fichiers et vous gérez la partie gzip par programmation, en gardant gzip les fichiers envoyés.

Quelques questions similaires pour la même raison: le site ASP.NET gèle parfois et / ou affiche un texte impair en haut de la page lors du chargement, sur des serveurs à charge équilibrée

Compression HTTP: Certains scripts externes / CSS ne décompressant pas correctement de temps en temps

(*) pourquoi ne pas le changer à nouveau? car à partir du moment où vous définissez un en-tête, vous ne pouvez pas participer, sauf si vous avez activé cette option sur IIS et que l’en-tête n’a pas tous été envoyé au navigateur.

Suivre

Si ce n’est pas gzipé, la prochaine chose qui m’est venue à l’esprit est que le fichier est envoyé et pour une raison quelconque, la connexion a été retardée, et elle a été expirée et fermée. Donc, vous obtenez “l’hôte distant fermé la connexion”.

Cela peut être résolu en fonction de la cause:

  1. Le client a vraiment fermé la connexion
  2. Le délai d’attente provient de la page elle-même, si vous utilisez le gestionnaire (encore une fois, le message doit probablement être “expiré de la page”).
  3. Le délai d’attente provient de la période d’inactivité en attente, la page prend plus que le temps d’exécution, obtient un délai d’attente et ferme la connexion. Peut-être que dans ce cas, le message était le Page Timed Out.
  4. Le pool effectue un recyclage dès que vous envoyez le fichier. Désactiver tous les cycles de piscine! C’est le cas le plus possible auquel je puisse penser en ce moment.

S’il provient de IIS, accédez aux propriétés du site Web et assurez-vous de définir le plus grand “Délai de connexion” et “Activer HTTP Keep-Alives”.

Le délai d’expiration de la page en modifiant le fichier web.config (vous pouvez le modifier par programme uniquement pour une page spécifique)

  

Jetez également un coup d'œil à: http://weblogs.asp.net/aghausman/archive/2009/02/20/prevent-request-timeout-in-asp-net.aspx

Verrouillage de session

Une autre chose que vous devez examiner est de ne pas utiliser la session sur le gestionnaire que vous utilisez pour envoyer le fichier, car la session verrouille l'action jusqu'à la fin et si un utilisateur prend plus de temps pour télécharger un fichier, un deuxième peut obtenir temps libre.

un parent:

appeler la page aspx pour renvoyer une image au hasard

Remplacer entièrement la session d'ASP.Net

La fonction Response.WriteFile échoue et donne le délai d'expiration de la passerelle 504

Ce que je voudrais faire, c’est utiliser la méthode ASP.NET Response.TransmitFile moins connue, car elle est très rapide (et utilise peut-être le cache du kernel IIS) et prend en charge toutes les fonctions d’en-tête. Il est basé sur l’API TransmitFile non gérée de Windows.

Mais pour pouvoir utiliser cette API, vous avez besoin d’un fichier physique à transférer. Voici donc un pseudo code c # qui explique comment procéder avec un chemin de fichier physique fictif myCacheFilePath. Il prend également en charge les possibilités de mise en cache du client. Bien sûr, si vous avez déjà un fichier en main, vous n’avez pas besoin de créer ce cache:

  if (!File.Exists(myCacheFilePath)) { LoadMyCache(...); // saves the file to disk. don't do this if your source is already a physical file (not stored in a db for example). } // we suppose user-agent (browser) cache is enabled // check appropriate If-Modified-Since header DateTime ifModifiedSince = DateTime.MaxValue; ssortingng ifm = context.Request.Headers["If-Modified-Since"]; if (!ssortingng.IsNullOrEmpty(ifm)) { try { ifModifiedSince = DateTime.Parse(ifm, DateTimeFormatInfo.InvariantInfo); } catch { // do nothing } // file has not changed, just send this information but truncate milliseconds if (ifModifiedSince == TruncateMilliseconds(File.GetLastWriteTime(myCacheFilePath))) { ResponseWriteNotModified(...); // HTTP 304 return; } } Response.ContentType = contentType; // set your file content type here Response.AddHeader("Last-Modified", File.GetLastWriteTimeUtc(myCacheFilePath).ToSsortingng("r", DateTimeFormatInfo.InvariantInfo)); // tell the client to cache that file // this API uses windows lower levels directly and is not memory/cpu intensive on Windows platform to send one file. It also caches files in the kernel. Response.TransmitFile(myCacheFilePath) 

Ce morceau de code fonctionne pour moi. Il lance le stream de données au client immédiatement. Il montre les progrès pendant le téléchargement. Il ne viole pas HTTP. L’en-tête Content-Length est spécifié et le codage de transfert tronqué n’est pas utilisé.

 protected void PrepareResponseStream(ssortingng clientFileName, HttpContext context, long sourceStreamLength) { context.Response.ClearHeaders(); context.Response.Clear(); context.Response.ContentType = "application/pdf"; context.Response.AddHeader("Content-Disposition", ssortingng.Format("filename=\"{0}\"", clientFileName)); //set cachebility to private to allow IE to download it via HTTPS. Otherwise it might refuse it //see reason for HttpCacheability.Private at http://support.microsoft.com/kb/812935 context.Response.Cache.SetCacheability(HttpCacheability.Private); context.Response.Buffer = false; context.Response.BufferOutput = false; context.Response.AddHeader("Content-Length", sourceStreamLength.ToSsortingng (System.Globalization.CultureInfo.InvariantCulture)); } protected void WriteDataToOutputStream(Stream sourceStream, long sourceStreamLength, ssortingng clientFileName, HttpContext context) { PrepareResponseStream(clientFileName, context, sourceStreamLength); const int BlockSize = 4 * 1024 * 1024; byte[] buffer = new byte[BlockSize]; int bytesRead; Stream outStream = m_Context.Response.OutputStream; while ((bytesRead = sourceStream.Read(buffer, 0, BlockSize)) > 0) { outStream.Write(buffer, 0, bytesRead); } outStream.Flush(); }