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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-10 01:21:04  来源:igfitidea点击:

How to download memorystream to a file?

c#asp.netmemorystream

提问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.CopyToBinaryWrite用这个替换你的电话:

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