J'utilise l'exemple de code ci-dessous pour écrire et télécharger un flux de mémoire dans un fichier en C #.
MemoryStream memoryStream = new MemoryStream();
TextWriter textWriter = new StreamWriter(memoryStream);
textWriter.WriteLine("Something");
byte[] bytesInStream = new byte[memoryStream.Length];
memoryStream.Write(bytesInStream, 0, bytesInStream.Length);
memoryStream.Close();
Response.Clear();
Response.ContentType = "application/force-download";
Response.AddHeader("content-disposition",
"attachment; filename=name_you_file.xls");
Response.BinaryWrite(bytesInStream);
Response.End();
Je reçois l'erreur suivante:
L'argument spécifié était en dehors de la plage de valeurs valides.
Nom du paramètre: offset
Quelle peut être la cause?
Au moment où, dans votre code, vous copiez les données dans un tableau, TextWriter n'a peut-être pas vidé les données. Cela se produira quand vous ferez couler () ou quand vous le fermerez ().
Voir si cela fonctionne:
MemoryStream memoryStream = new MemoryStream();
TextWriter textWriter = new StreamWriter(memoryStream);
textWriter.WriteLine("Something");
textWriter.Flush(); // added this line
byte[] bytesInStream = memoryStream.ToArray(); // simpler way of converting to array
memoryStream.Close();
Response.Clear();
Response.ContentType = "application/force-download";
Response.AddHeader("content-disposition", "attachment; filename=name_you_file.xls");
Response.BinaryWrite(bytesInStream);
Response.End();
Vous faites logiquement quelque chose de mal ici. Commencez par écrire du texte dans MemoryStream
, puis un tableau vide dans le même flux. Je suppose que vous essayez de copier le contenu du flux dans le tableau bytesInStream
. Vous pouvez créer ce tableau en appelant memoryStream.ToArray()
.
Vous pouvez également éviter la copie du tableau en écrivant le flux directement dans le flux de sortie de la réponse à l'aide de MemoryStream.CopyTo
. Remplacez votre appel BinaryWrite
par ceci:
memoryStream.Position = 0;
memoryStream.CopyTo(Response.OutputStream);
Remarque: positionnez explicitement le flux au début car CopyTo
sera copié à partir de la position actuelle.
OK, étant donné que l’on obtient évidemment un vote négatif pour avoir fourni uniquement un exemple concret, laissez-moi élaborer:
Tout d'abord, vous ne faites pas
textWriter.Flush()
et attendez-vous à ce que le contenu de textwriter ait été vidé en mémoire de flux.
Alors tu ne fais pas
memoryStream.Position = 0
Et attendez-vous à ce que le flux de mémoire soit "écrit" depuis la position 0.
Alors tu fais
memoryStream.Write(bytesInStream, 0, bytesInStream.Length);
mais ce que vous voulez dire, c'est
memoryStream.Read(bytesInStream, 0, CInt(memoryStream.Length))
Vous avez également oublié que Long est long, alors que read utilise un entier, vous pouvez donc obtenir une exception.
Donc, ceci est votre code minimalement adapté au "travail" (je l'ai copié dans un projet vb)
Imports System.Web
Imports System.Web.Services
Public Class TextHandler
Implements System.Web.IHttpHandler
Sub ProcessRequest(ByVal context As HttpContext) Implements IHttpHandler.ProcessRequest
'context.Response.ContentType = "text/plain"
'context.Response.Write("Hello World!")
Dim memoryStream As New System.IO.MemoryStream()
Dim textWriter As System.IO.TextWriter = New System.IO.StreamWriter(memoryStream)
textWriter.WriteLine("Something")
textWriter.Flush()
memoryStream.Position = 0
Dim bytesInStream As Byte() = New Byte(memoryStream.Length - 1) {}
'memoryStream.Write(bytesInStream, 0, bytesInStream.Length)
memoryStream.Read(bytesInStream, 0, CInt(memoryStream.Length))
memoryStream.Close()
context.Response.Clear()
context.Response.ContentType = "application/force-download"
context.Response.AddHeader("content-disposition", "attachment; filename=name_you_file.txt")
context.Response.BinaryWrite(bytesInStream)
context.Response.End()
End Sub
ReadOnly Property IsReusable() As Boolean Implements IHttpHandler.IsReusable
Get
Return False
End Get
End Property
End Class
Ensuite, vous utilisez
Content-Type: application/force-download
ce qui signifie
"Moi, le serveur Web, je vais vous mentir (sur le navigateur) sur le choix de ce fichier Afin que vous ne le traitiez pas comme un fichier PDF/Word Document/MP3/peu importe et invitez l'utilisateur à enregistrer le fichier mystérieux sur le disque à la place ". C'est un sale bidouille qui casse horriblement quand le le client ne fait pas "enregistrer sur le disque".
...
Enfin, vous ne codez pas correctement le nom de fichier. Par conséquent, si vous utilisez des caractères non-ASCII pour le nom de fichier, le nom de fichier sera brouillé, ce qui est très amusant si vous êtes chinois ou russe et que vous opérez entièrement en dehors du ASCII jeu de caractères.
Voici un extrait rapide de l’un de mes gestionnaires ajax . C’est VB.NET, lors de la conversion, prenez garde à la longueur - 1 chose.
Sub ProcessRequest(ByVal context As HttpContext) Implements IHttpHandler.ProcessRequest
Dim strFileName As String = "Umzugsmitteilung.doc"
Dim strUID As String = context.Request.QueryString("ump_uid")
context.Response.Clear()
'If String.IsNullOrEmpty(strUID) Or fileData Is Nothing Then
' context.Response.Write("<script type=""text/javascript"">alert('File does not exist !')</script>")
' context.Response.End()
'End If
context.Response.ClearContent()
'context.Response.AddHeader("Content-Disposition", "attachment; filename=" + strFileName)
context.Response.AddHeader("Content-Disposition", GetContentDisposition(strFileName))
'context.Response.ContentType = "application/msword"
context.Response.ContentType = "application/octet-stream"
GetUmzugsMitteilung(strUID)
context.Response.End()
End Sub ' ProcessRequest
Public Shared Sub SaveWordDocumentToOutputStream(strUID As String, doc As Aspose.Words.Document)
Using ms As System.IO.MemoryStream = New System.IO.MemoryStream()
CreateWordDocumentFromTemplate(strUID, doc, ms)
ms.Position = 0
Dim bytes As Byte() = New Byte(ms.Length - 1) {}
ms.Read(bytes, 0, CInt(ms.Length))
System.Web.HttpContext.Current.Response.OutputStream.Write(bytes, 0, ms.Length)
ms.Close()
End Using ' ms
End Sub ' SaveWordDocumentToOutputStream
Public Shared Function StripInvalidPathChars(str As String) As String
If str Is Nothing Then
Return Nothing
End If
Dim strReturnValue As String = ""
Dim strInvalidPathChars As New String(System.IO.Path.GetInvalidPathChars())
Dim bIsValid As Boolean = True
For Each cThisChar As Char In str
bIsValid = True
For Each cInvalid As Char In strInvalidPathChars
If cThisChar = cInvalid Then
bIsValid = False
Exit For
End If
Next cInvalid
If bIsValid Then
strReturnValue += cThisChar
End If
Next cThisChar
Return strReturnValue
End Function ' StripInvalidPathChars
Public Shared Function GetContentDisposition(ByVal strFileName As String) As String
' http://stackoverflow.com/questions/93551/how-to-encode-the-filename-parameter-of-content-disposition-header-in-http
Dim contentDisposition As String
strFileName = StripInvalidPathChars(strFileName)
If System.Web.HttpContext.Current IsNot Nothing AndAlso System.Web.HttpContext.Current.Request.Browser IsNot Nothing Then
If (System.Web.HttpContext.Current.Request.Browser.Browser = "IE" And (System.Web.HttpContext.Current.Request.Browser.Version = "7.0" Or System.Web.HttpContext.Current.Request.Browser.Version = "8.0")) Then
contentDisposition = "attachment; filename=" + Uri.EscapeDataString(strFileName).Replace("'", Uri.HexEscape("'"c))
ElseIf (System.Web.HttpContext.Current.Request.Browser.Browser = "Safari") Then
contentDisposition = "attachment; filename=" + strFileName
Else
contentDisposition = "attachment; filename*=UTF-8''" + Uri.EscapeDataString(strFileName)
End If
Else
contentDisposition = "attachment; filename*=UTF-8''" + Uri.EscapeDataString(strFileName)
End If
Return contentDisposition
End Function ' GetContentDisposition
Vous utilisez un très long chemin pour convertir une chaîne en octets.
Etes-vous sûr que vous avez besoin de flux? Pourquoi ne pas utiliser l'encodage?
Response.BinaryWrite(Encoding.UTF8.GetBytes("Something"))