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指定的参数超出了有效值的范围。参数名称:offset
What may be the cause?
可能是什么原因?
4 个解决方案
#1
23
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();
#2
6
You are doing something wrong logically here. First, you write some text to the MemoryStream
and then you write an empty array to the same stream. I assume you are trying to copy the contents of the stream into the bytesInStream
array. 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 stream using MemoryStream.CopyTo
. Replace your BinaryWrite
call with this:
或者,您可以通过使用MemoryStream.CopyTo将流直接写入响应输出流来避免阵列复制。用这个替换你的BinaryWrite调用:
memoryStream.Position = 0;
memoryStream.CopyTo(Response.OutputStream);
Note: explicitly position the stream at the start since CopyTo
will copy from the current position.
注意:由于CopyTo将从当前位置复制,因此在开始时显式定位流。
#3
2
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字符集之外操作,这非常有趣。
Original
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://*.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
0
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( “东西”))
#1
23
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();
#2
6
You are doing something wrong logically here. First, you write some text to the MemoryStream
and then you write an empty array to the same stream. I assume you are trying to copy the contents of the stream into the bytesInStream
array. 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 stream using MemoryStream.CopyTo
. Replace your BinaryWrite
call with this:
或者,您可以通过使用MemoryStream.CopyTo将流直接写入响应输出流来避免阵列复制。用这个替换你的BinaryWrite调用:
memoryStream.Position = 0;
memoryStream.CopyTo(Response.OutputStream);
Note: explicitly position the stream at the start since CopyTo
will copy from the current position.
注意:由于CopyTo将从当前位置复制,因此在开始时显式定位流。
#3
2
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字符集之外操作,这非常有趣。
Original
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://*.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
0
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( “东西”))