Après quelques tests, j'ai trouvé la solution suivante au problème. TransmitFile() a une limitation importante : il lit tout le fichier en mémoire avant de l'envoyer, ce qui est vraiment mauvais pour les fichiers volumineux. Donc en gros, j'ai dû recourir au fractionnement manuel et vérifier si le client est connecté après chaque fraction.
context.Response.Clear();
context.Response.BufferOutput = false;
context.Response.ContentType = "application/octet-stream";
context.Response.AddHeader("Content-Disposition", "attachment; filename=" + originalFilename);
context.Response.AddHeader("Content-Length", fileLength.ToString());
context.Response.Cache.SetNoStore();
context.Response.Flush();
downloadFailed = !context.Response.IsClientConnected;
int thisChunk;
long offset = 0;
int chunkSize = 1024 * 8;
byte[] bytes = new byte[chunkSize];
FileStream r = File.OpenRead(localFilename);
while((offset < fileLength) && !downloadFailed)
{
if((fileLength - offset) < chunkSize)
{
thisChunk = (int)(fileLength - offset);
}
else
{
thisChunk = chunkSize;
}
r.Read(bytes, 0, chunkSize);
try
{
context.Response.BinaryWrite(bytes);
context.Response.Flush();
if(!context.Response.IsClientConnected)
{
downloadFailed = true;
}
}
catch(ObjectDisposedException ex1)
{
// Le flux est fermé, rien n'a été écrit
break;
}
catch(System.IO.IOException ex3)
{
// Erreur d'E/S, état inconnu, abandon
Trace.Write(ex3);
break;
}
offset += thisChunk;
}
if(!downloadFailed)
{
// Mettre à jour le fichier, les statistiques, etc.
}
context.Response.Flush();
HttpContext.Current.ApplicationInstance.CompleteRequest();
Il faudra jouer un peu avec la taille du fragment pour trouver la taille optimale. Mais en gros, cela fonctionne de manière fiable comme ça.