web-dev-qa-db-fra.com

Comment télécharger memorystream dans un fichier?

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?

12
user1357872

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();
24
Henrik

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.

6
Peter Lillevold

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.


Original

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
3
Stefan Steiger

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"))

0
Dennis