如何将内存流下载到文件?

时间:2021-08-05 16:47:28

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( “东西”))