2013-05-15 22 views
9

Sto utilizzando il seguente codice di esempio per scrivere e scaricare un flusso di memoria in un file in C#.Come scaricare memorystream in un file?

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

Sto ottenendo il seguente errore:

Specified argument was out of the range of valid values.
Parameter name: offset

Quale può essere la causa?

+0

Che cosa significa il debugger detto? La dimensione di 'bytesInStream' è maggiore di 0? –

+0

@Kiarash: no, il terzo parametro è il numero di elementi da scrivere (ref http://msdn.microsoft.com/en-us/library/system.io.memorystream.write.aspx) –

+0

dove viene visualizzato l'errore Intendo quale linea? – Kiarash

risposta

20

Nel punto del codice in cui si copiano i dati su un array, il TextWriter potrebbe non aver svuotato i dati. Questo accadrà quando si lava() o quando si chiude().

vedere se questo funziona:

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(); 
+0

Come ricordo, funziona bene su Windows 7, ma non funziona su XP (Windows Server 2003). Inoltre, non mettete ms.position = 0. –

+0

@Quandary - se leggete sulla documentazione di 'ToArray' vedrete che il posizionamento del flusso non è necessario. Cito: "Scrive il contenuto del flusso in un array di byte, indipendentemente dalla proprietà Position." –

+0

@Peter Lillevold: Make that: dovrebbe scrivere il Byte Array indipendentemente dalla posizione, e lo fa anche su Windows 7. –

2

OK, dal momento che uno ottiene ovviamente downvoted per fornire solo un esempio di lavoro, Vorrei esporre:

In primo luogo, non si fa

textWriter.Flush() 

e si aspettano il Contenuto di TextWriter essere stata lavata al memorystream.

Allora non fai

memoryStream.Position = 0 

e si aspettano il MemoryStream per essere "scritto" da posizione 0.

Allora non

memoryStream.Write(bytesInStream, 0, bytesInStream.Length); 

ma quello che effettivamente dire è

memoryStream.Read(bytesInStream, 0, CInt(memoryStream.Length)) 

Hai anche perso la lunghezza è lunga, mentre la lettura utilizza un numero intero, in modo da poter ottenere un'eccezione lì.

quindi questo è il codice minimamente adattato al "lavoro" (ho copiato in un progetto 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 

quindi si utilizza

Content-Type: application/force-download 

che significa

"I, the web server, am going to lie to you (the browser) about what this file is so that you will not treat it as a PDF/Word Document/MP3/whatever and prompt the user to save the mysterious file to disk instead". It is a dirty hack that breaks horribly when the client doesn't do "save to disk".

...

Infine, non si codifica correttamente il nome del file, quindi se si usano caratteri non ASCII per il nome del file, questo verrà alterato nel nome del file, il che è molto divertente se si è cinesi o russi e si opera interamente al di fuori del set di caratteri ASCII .


originale

Ecco un rapido estratto da uno dei miei gestori di Ajax. È VB.NET, durante la conversione, fai attenzione alla lunghezza -1 delle cose.

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 
+4

Quindi l'OP deve tirare fuori i bit rilevanti dal tuo codice? Penso che dovresti farlo e spiegare cosa differisce e perché. – CodeCaster

+0

@CodeCaster: Beh, in parole povere, ha dimenticato ms.Position = 0 e non codifica correttamente il nome del file. –

6

Si sta facendo qualcosa di sbagliato logicamente qui. Innanzitutto, scrivi del testo nello MemoryStream e poi scrivi un array vuoto nello stesso flusso. Presumo che tu stia cercando di copiare il contenuto dello stream nell'array bytesInStream. È possibile creare questo array chiamando memoryStream.ToArray().

In alternativa, è possibile evitare la copia dell'array scrivendo il flusso direttamente su the response output stream utilizzando MemoryStream.CopyTo.Sostituire il BinaryWrite chiamata con questo:

memoryStream.Position = 0; 
memoryStream.CopyTo(Response.OutputStream); 

Nota: posizionare in modo esplicito il flusso in partenza dal CopyTo copierà dalla posizione corrente.

+0

Puoi fornire un esempio? – user1357872

+0

Avrai bisogno di cercare il flusso di memoria all'inizio usando '.Seek' per' CopyTo' per copiare qualsiasi cosa. – Joshua

+0

Non è necessario alcun esempio, basta inserire ms.position = 0 in là. –

0

Stai usando una strada molto lunga per convertire stringa in byte.
Sei sicuro di aver bisogno di flussi? Perché non usare la codifica?

Response.BinaryWrite(Encoding.UTF8.GetBytes("Something"))

Problemi correlati