2013-05-15 131 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"))

相关问题