C# 如何将内存流下载到文件?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/16558317/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to download memorystream to a file?
提问by user1357872
I'm using the below sample code for writing and downloading a memory stream to a file in C#.
我正在使用以下示例代码在 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();
I am getting the following error:
我收到以下错误:
Specified argument was out of the range of valid values.
Parameter name: offset
指定的参数超出了有效值的范围。
参数名称:偏移量
What may be the cause?
可能是什么原因?
采纳答案by Henrik
At the point in your code where you copy the data to an array, the TextWriter might not have flushed the data. This will happen when you Flush() or when you Close() it.
在代码中将数据复制到数组的位置,TextWriter 可能尚未刷新数据。当您 Flush() 或 Close() 时会发生这种情况。
See if this works:
看看这是否有效:
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();
回答by Stefan Steiger
OK, since one obviously gets downvoted for providing just a working example, let me Elaborate:
好的,因为一个人显然因为提供一个工作示例而被否决,让我详细说明:
First, you don't do
首先,你不做
textWriter.Flush()
and expect the Content of textwriter to have been flushed to memorystream.
并期望 textwriter 的内容已刷新到内存流。
Then you don't do
那你不做
memoryStream.Position = 0
And expect the memorystream to be "written" from Position 0.
并期望从位置 0“写入”内存流。
Then you do
然后你做
memoryStream.Write(bytesInStream, 0, bytesInStream.Length);
but what you actually mean is
但你真正的意思是
memoryStream.Read(bytesInStream, 0, CInt(memoryStream.Length))
You also missed that length is Long, while read uses an integer, so you can get an exception there.
您还错过了长度为 Long 的情况,而 read 使用整数,因此您可以在那里获得异常。
So this is your code minimally adapted to "work" (i copied it into a vb Project)
所以这是你的代码最低限度地适应“工作”(我将它复制到一个 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
Then you use
然后你用
Content-Type: application/force-download
which means
意思是
"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".
“我,网络服务器,会向你(浏览器)说谎这个文件是什么,这样你就不会把它当作 PDF/Word 文档/MP3/任何东西,并提示用户将神秘文件保存到磁盘反而”。这是一个肮脏的黑客,当客户端不执行“保存到磁盘”时会严重破坏。
...
...
And finally, you don't encode the filename correctly, so if one uses non-ASCII characters for the filename, it will garble the filename, which is very funny if you happen to be Chinese or Russian and operate entirely outside the ASCII character set.
最后,你没有正确编码文件名,所以如果使用非 ASCII 字符作为文件名,它会造成文件名乱码,如果你碰巧是中文或俄语并且完全在 ASCII 字符集之外操作,这会很有趣.
原来的
Here a quick excerpt from one of my ajax handlers. It's VB.NET, when converting, take care on the length -1 things.
这是我的一个 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
回答by Peter Lillevold
You are doing something wrong logically here. First, you write some text to the MemoryStreamand then you write an empty array to the same stream. I assume you are trying to copy the contents of the stream into the bytesInStreamarray. You can create this array by calling memoryStream.ToArray().
你在这里逻辑上做错了。首先,向 写入一些文本MemoryStream,然后向同一个流写入一个空数组。我假设您正在尝试将流的内容复制到bytesInStream数组中。您可以通过调用创建此数组memoryStream.ToArray()。
Alternatively, you can avoid the array copying by writing the stream directly to the response output streamusing MemoryStream.CopyTo. Replace your BinaryWritecall with this:
或者,您可以通过使用 将流直接写入响应输出流来避免数组复制MemoryStream.CopyTo。BinaryWrite用这个替换你的电话:
memoryStream.Position = 0;
memoryStream.CopyTo(Response.OutputStream);
Note: explicitly position the stream at the start since CopyTowill copy from the current position.
注意:在开始时明确定位流,因为CopyTo将从当前位置复制。
回答by Dennis
You're using a very long way to convert string to bytes.
Are you sure, that you need any streams? Why just don't use encoding?
您正在使用很长的方法将字符串转换为字节。
您确定需要任何流吗?为什么不使用编码?
Response.BinaryWrite(Encoding.UTF8.GetBytes("Something"))
Response.BinaryWrite(Encoding.UTF8.GetBytes("Something"))

