Comment livrer de gros fichiers en ASP.NET réponse?

Je ne suis pas à la recherche d'une alternative de contenu de fichier base de données, en effet je suis à la recherche de la racine du problème, cela a été l'exécution de dossier jusqu'à IIS 6 où nous avons couru notre application en mode classique, maintenant nous mis à jour notre IIS à 7 et nous exécutons piscine app 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 fais face aux problèmes suivants,

fichiers sont de taille moyenne 4 à 100 MB, donc permet d'envisager 80MB fichier cas de téléchargement.

Mise En Mémoire Tampon, Démarrage Lent

Response.BufferOutput = True;

cela se traduit par un démarrage très lent du fichier, les téléchargements de l'utilisateur et même la barre de progression n'apparaît que quelques secondes, généralement de 3 à 20 secondes, la raison derrière est, IIS lit le fichier entier d'abord, détermine la longueur du contenu, puis commence le transfert de fichier. Le fichier est diffusé en vidéo player, et il fonctionne très très lent, mais iPad ne télécharge que la fraction de fichier en premier donc il fonctionne rapidement.

Mise En Mémoire Tampon, Aucune Content-Length, Démarrage Rapide, Pas De Progrès

Reponse.BufferOutput = False;

il en résulte un démarrage immédiat, mais le client final (navigateur typique comme Chrome) ne connaît pas la longueur du contenu car IIS ne le sait pas non plus, il n'affiche donc pas de progrès, mais il dit x KB téléchargé.

mise en mémoire Tampon Off, Manuel Content-Length, Démarrage Rapide, le Progrès et la Violation de Protocole

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

il en résulte un téléchargement immédiat correct du fichier dans Chrome etc, mais dans certains cas IIS handler entraîne une erreur de" connexion fermée client à distance " (ce qui est très fréquent) et D'autres WebClient entraîne une violation du protocole. cela se produit 5 à 10% de toutes les requêtes, pas toutes les requêtes.

je suppose que ce qui se passe est, IIS n'envoie rien appelé 100 continuer quand nous ne faisons pas de tampon et le client pourrait déconnecter ne pas attendre une sortie. Cependant, la lecture de fichiers à partir de la source peut prendre plus de temps, mais du côté du client, j'ai augmenté le temps d'attente, mais il semble que le temps D'attente de l'IIS soit plus long et que je n'ai aucun contrôle.

y a-t-il quoi qu'il en soit que je puisse forcer la réponse pour envoyer 100 continuer et ne laisser personne fermer la connexion?

mise à JOUR

j'ai trouvé les en-têtes suivants dans Firefox/Chrome, rien ne semble inhabituel ici pour Violation de protocole ou mauvais en-tête.

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

UPDATE 2

recyclage tournant n'a pas encore offert beaucoup, mais j'ai augmenté mon MaxWorkerProcess à 8 et je reçois maintenant moins d'erreurs que par le passé.

mais en moyenne, sur 200 requêtes en une seconde, 2 pour 10 demandes d'échouer.. et ça arrive presque toutes les secondes.

UPDATE 3

Continue 5% des demandes à défaut avec "Le serveur a commis une violation de protocole. Section=Responsetatusline", j'ai un autre programme qui télécharge du contenu à partir du webserver qui utilise WebClient, et qui donne cette erreur 4-5 fois par seconde, sur une moyenne, j'ai 5% de requêtes échouant. Y at-il de toute façon de tracer WebClient l'échec?

Problèmes Redéfinis

Fichier De Zéro Octet Reçu

IIS ferme la connexion pour une raison quelconque, du côté du client dans WebConfig, je reçois 0 bytes pour le fichier qui n'est pas zéro bytes, nous faisons sha1 vérification hash, cela nous a dit que dans IIS serveur web, aucune erreur n'est enregistrée.

C'était mon erreur, et son résolu puisque nous utilisons le cadre de L'entité, il lisait sale (lignes non engagées) comme lire n'était pas dans la portée de la transaction, le mettre dans la portée de la transaction a résolu cette question.

Violation Du Protocole Exception Soulevée

WebClient lance WebException disant "le serveur a commis une violation de protocole. Section=Respondestatusline.

je sais que je peux activer parsing d'en-tête dangereux, mais ce n'est pas le point, lorsque c'est mon gestionnaire HTTP qui envoie les en-têtes appropriés, ne sait pas pourquoi IIS envoie quelque chose de plus (vérifié sur firefox et chrome, rien d'inhabituel), cela se produit seulement 2% des fois.

UPDATE 4

a trouvé erreur sc-win32 64 et j'ai lu quelque part que WebLimits pour MinBytesPerSecond doit être changé de 240 à 0, toujours tout est pareil. Toutefois, j'ai remarqué que chaque fois que IIS enregistre une erreur 64 sc-win32, IIS enregistre L'état HTTP 200 mais il y a eu une erreur. Maintenant, je ne peux pas activer la journalisation de Trace ratée pour 200 parce qu'il en résultera des fichiers massifs.

les deux problèmes ci-dessus ont été résolus en augmentant la Minbytesperseconde et en plus des Sessions invalidantes, j'ai ajouté une réponse détaillée résumant chaque point.

40
demandé sur Akash Kava 2012-03-07 15:42:19

4 réponses

bien que la façon correcte de livrer les gros fichiers dans IIS est l'option suivante,

  1. mettre MinBytesPerSecond à zéro dans les limites du Web (cela aidera certainement à améliorer les performances, car IIS choisit de fermer les clients qui détiennent des connexions KeepAlive avec des transferts de plus petite taille)
  2. allouer plus de processus Worker au Pool D'applications, j'ai mis à 8, Maintenant cela ne devrait être fait que si votre serveur distribue des fichiers plus gros. Ce sera certainement causer d'autres sites à effectuer plus lent, mais cela assurera de meilleures livraisons. Nous avons mis à 8 car ce serveur n'a qu'un seul site web et il ne fournit que des fichiers énormes.
  3. désactiver le Pool d'applications de Recyclage
  4. désactiver les Sessions
  5. Congé De Mise En Mémoire Tampon
  6. avant chacune des étapes suivantes, vérifier la réponse.IsClientConnected est vraie, sinon abandonner et de ne pas envoyer quoi que ce soit.
  7. Set Content-Length avant d'envoyer le fichier
  8. Vider la Réponse
  9. Écrire pour les Flux de Sortie, et Rincer à intervalles réguliers
12
répondu Akash Kava 2012-03-15 10:13:15

lorsque vous avez défini la longueur du contenu avec le bufferOutput à false, alors la raison possible de l'échec est que IIS essaye de gzip le fichier que vous envoyez, et en définissant le contenu-Length IIS ne peut pas le ramener à un contenu comprimé, et les erreurs commencent (*).

So keep the BufferOutput to false, and second disable the gzip from iis for the files you send - ou désactivez l'IIS gzip pour tous les fichiers et vous manipulez la partie gzip programmatiquement, en gardant hors de gzip les fichiers que vous envoyez.

quelques questions similaires pour la même raison: ASP.NET site parfois congélation et / ou affichage de texte Impair en haut de la page lors du chargement, sur la charge des serveurs équilibrés

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

( * ) pourquoi ne pas le changer à nouveau ? parce qu'à 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 vous prenez le cas où l'en-tête n'ont pas tous prêts envoyer au navigateur.

suivi

si ce n'est pas gziped, la prochaine chose qui m'est venue à l'esprit est que le dossier est envoyé et pour une raison quelconque la connexion a été retardée, et a obtenu un délai et fermé. Donc, vous obtenez le "hôte distant fermé la connexion".

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

  1. Client vraiment fermé la connexion
  2. le timeout est à partir de la page elle-même, si vous utilisez handler (encore, probablement, le message doit être "page Timed Out" ).
  3. le timeout vient de l'attente au ralenti, la page prend plus que le temps d'exécution, obtient un timeout et ferme la connexion. Peut-être que dans ce cas, le message était la Page programmée.
  4. La piscine faire recycler le moment d'envoyer le fichier. Désactivez tout recyclage de piscine! Ce sont les cas les plus possibles que je pense dès maintenant.

si cela vient de L'IIS, allez dans les propriétés du site web et assurez-vous de définir le plus grand "délai de connexion", et "activer les serveurs HTTP".

Le délai de page en changeant le web.config (vous pouvez le modifier par programmation seulement pour une page spécifique)

<httpRuntime executionTimeout="43200"

aussi jeter un oeil à : http://weblogs.asp.net/aghausman/archive/2009/02/20/prevent-request-timeout-in-asp-net.aspx

Session de verrouillage

une autre chose que vous devez examiner est de ne pas utiliser session sur le Gestionnaire que vous utilisez pour envoyer le fichier, parce que la session verrouille l'action jusqu'à la fin et si un utilisateur prend plus de temps pour télécharger un fichier, une seconde peut obtenir un temps d'arrêt.

un parent quelconque:

appeler aspx page pour retourner une image au hasard lent

remplaçant ASP.Net 's session entirely

réponse.La fonction WriteFile échoue et donne 504 gateway time-out

10
répondu Aristos 2017-05-23 12:34:06

ce que je ferais c'est utiliser le moins connu ASP.NET réponse.TransmitFile méthode, car il est très rapide (et utilise éventuellement le cache du noyau IIS) et prend soin de tous les trucs d'en-tête. Il est basé sur L'API Windows TransmitFile .

mais pour pouvoir utiliser cette API, vous avez besoin d'un fichier physique à transférer. Voici donc un pseudo code c # qui explique comment faire cela avec un fichier physique fictif de myCacheFilePath chemin. Il prend également en charge les possibilités de mise en cache client. Bien sûr, si vous avez déjà un fichier sous la 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;
    string ifm = context.Request.Headers["If-Modified-Since"];
    if (!string.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).ToString("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)
4
répondu Simon Mourier 2012-03-11 18:05:24

ce code fonctionne pour moi. Il démarre le flux de données vers le 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 l'encodage de transfert tronqué n'est pas utilisé.

protected void PrepareResponseStream(string clientFileName, HttpContext context, long sourceStreamLength)
{
    context.Response.ClearHeaders();
    context.Response.Clear();

    context.Response.ContentType = "application/pdf";
    context.Response.AddHeader("Content-Disposition", string.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.ToString    (System.Globalization.CultureInfo.InvariantCulture));
}

protected void WriteDataToOutputStream(Stream sourceStream, long sourceStreamLength, string 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();
}
2
répondu HANiS 2013-01-16 15:23:42