2013-05-15 135 views
9

我正在使用下面的示例代碼編寫和下載一個內存流到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(); 

我收到以下錯誤:

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

可能是什麼原因?

+0

調試器告訴你什麼? 'bytesInStream'的大小是否大於0? –

+0

@Kiarash - 不,第三個參數是要寫入的元素數(參考http://msdn.microsoft.com/en-us/library/system.io.memorystream.write.aspx) –

+0

你在哪裏得到錯誤我的意思是哪條線? – Kiarash

回答

20

在代碼中將數據複製到數組的位置,TextWriter可能沒有刷新數據。當你刷新()或關閉()它時,會發生這種情況。

見,如果這個工程:

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

我記得,在Windows 7上工作正常,但在XP(Windows Server 2003)上失敗。此外,你不要把ms.position = 0。 –

+0

@Quandary - 如果你閱讀了「ToArray」的文檔,你會發現定位流是不必要的。我引用:「將流內容寫入字節數組,無論Position屬性如何。」 –

+0

@Peter Lillevold:做到這一點:應該寫無論位置的字節數組,甚至在Windows 7上做到這一點。 –

2

OK,因爲一個明顯得到downvoted提供只是一個工作的例子,讓我解釋:

首先,你不這樣做

textWriter.Flush() 

和期望的TextWriter的內容已經被刷新記憶流。

然後你不這樣做

memoryStream.Position = 0 

,並期望將MemoryStream從位置0

然後「寫」你

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

但實際上你的意思是

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

你也錯過了那個長度很長,而閱讀使用整數,所以你可以在那裏得到一個異常。

因此,這是你的代碼最小修改爲 「工作」(我它複製到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 

然後使用

Content-Type: application/force-download 

這意味着

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

...

最後,你不會正確地編碼文件名,所以如果一個人使用非ASCII字符作爲文件名,它會亂碼文件名,如果你碰巧是中文或俄文,並且完全在ASCII字符集之外操作,這將非常有趣。


原始

這裏快速摘自我的ajax處理程序之一。 這是VB.NET,轉換時,注意長度爲-1的東西。

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

因此,OP的必須從你的代碼中拉出相關的位?我認爲你應該這樣做,並解釋什麼不同以及爲什麼。 – CodeCaster

+0

@CodeCaster:簡單地說,他忘了ms.Position = 0,並且他沒有正確編碼文件名。 –

6

你在邏輯上做錯了事。首先,你寫一些文本到MemoryStream,然後你寫一個空數組到同一個流。我假設你正試圖將流的內容複製到bytesInStream陣列中。你可以通過調用memoryStream.ToArray()來創建這個數組。

或者,您可以通過使用MemoryStream.CopyTo將流直接寫入the response output stream來避免數組複製。這種替換您的通話BinaryWrite

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

注:流在開始明確定位,因爲CopyTo會從當前位置複製。

+0

你能提供一個例子嗎? – user1357872

+0

您需要使用'.Seek'來查找內存流,才能真正複製任何內容。 – Joshua

+0

沒有需要的例子,只需把ms.position = 0放在那裏。 –

0

您使用了很遠的路到字符串轉換爲字節。
你確定,你需要任何流?爲什麼不使用編碼?

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

相關問題