Как загрузить memorystream в файл?

Я использую приведенный ниже пример кода для записи и загрузки потока памяти в файл на 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();

Я получаю следующую ошибку:

Указанный аргумент находится вне диапазона допустимых значений.
Имя параметра: смещение

Что может быть причиной?


person user1357872    schedule 15.05.2013    source источник
comment
Что вам говорит отладчик? Размер bytesInStream больше 0?   -  person Tieson T.    schedule 15.05.2013
comment
@Kiarash - нет, третий параметр - это количество элементов для записи (ref msdn.microsoft.com/en-us/library/)   -  person Peter Lillevold    schedule 15.05.2013
comment
где вы получаете ошибку я имею в виду, какую строку?   -  person Kiarash    schedule 15.05.2013
comment
В строке Response.BinaryWrite(bytesInStream);   -  person user1357872    schedule 15.05.2013
comment
Я не уверен, что вы пытаетесь здесь сделать, но вы пропустили, что MemoryStream имеет ToArray, который дает вам эквивалент byte[]?   -  person Damien_The_Unbeliever    schedule 15.05.2013
comment
вы пропустили textWriter.Flush(); после textWriter.WriteLine(что-то); иначе ваш поток памяти будет пуст, и вы получите ошибку   -  person Kiarash    schedule 15.05.2013


Ответы (4)


В том месте вашего кода, где вы копируете данные в массив, TextWriter может не сбросить данные. Это произойдет, когда вы Flush() или когда вы Close() его.

Посмотрите, работает ли это:

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();
person krembanan    schedule 15.05.2013
comment
Насколько я помню, это прекрасно работает в Windows 7, но не работает в XP (Windows Server 2003). Кроме того, вы не ставите ms.position = 0. - person Stefan Steiger; 15.05.2013
comment
@Quandary - если вы прочитаете документацию для ToArray, вы увидите, что позиционирование потока не требуется. Цитирую: Записывает содержимое потока в массив байтов независимо от свойства Position. - person Peter Lillevold; 15.05.2013
comment
@Peter Lillevold: Сделайте это: должен записывать массив байтов независимо от позиции, и даже делает это в Windows 7. - person Stefan Steiger; 15.05.2013
comment
Спасибо как @PeterLillevold, так и (at)Quandary за очень хорошие отзывы, я не знал, где здесь различия ОС. Любые предложения по изменению кода выше, чтобы сделать его более безопасным? - person krembanan; 15.05.2013

Вы делаете что-то не так логически здесь. Сначала вы записываете некоторый текст в MemoryStream, а затем в тот же поток записываете пустой массив. Я предполагаю, что вы пытаетесь скопировать содержимое потока в массив bytesInStream. Вы можете создать этот массив, вызвав memoryStream.ToArray().

Кроме того, вы можете избежать копирования массива, записав поток напрямую в поток вывода ответа с использованием MemoryStream.CopyTo. Замените свой вызов BinaryWrite следующим:

 memoryStream.Position = 0;
 memoryStream.CopyTo(Response.OutputStream);

Примечание: явно поместите поток в начало, так как CopyTo будет копировать из текущей позиции.

person Peter Lillevold    schedule 15.05.2013
comment
Можете ли вы привести пример? - person user1357872; 15.05.2013
comment
Вам нужно будет найти поток памяти в начало, используя .Seek для CopyTo, чтобы что-то скопировать. - person Joshua; 15.05.2013
comment
Пример не нужен, просто введите ms.position = 0. - person Stefan Steiger; 15.05.2013
comment
@Quandary - если вы прочитаете документацию для ToArray, вы увидите, что позиционирование потока не требуется. Цитирую: Записывает содержимое потока в массив байтов независимо от свойства Position. Также здесь хорош пример, так как я предлагаю другой подход, чем копирование в промежуточный массив. - person Peter Lillevold; 15.05.2013

ОК, так как за один из них явно проголосовали за предоставление только рабочего примера, позвольте мне уточнить:

Во-первых, вы не делаете

textWriter.Flush()

и ожидайте, что содержимое текстового редактора будет сброшено в поток памяти.

Тогда вы не делаете

memoryStream.Position = 0

И ожидайте, что поток памяти будет «записан» с позиции 0.

Тогда вы делаете

memoryStream.Write(bytesInStream, 0, bytesInStream.Length);

но что вы на самом деле имеете в виду

memoryStream.Read(bytesInStream, 0, CInt(memoryStream.Length))

Вы также пропустили, что длина — это Long, а чтение использует целое число, поэтому вы можете получить исключение.

Итак, это ваш код, минимально адаптированный для «работы» (я скопировал его в проект 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 

что значит

«Я, веб-сервер, собираюсь солгать вам (браузеру) о том, что это за файл, чтобы вы не рассматривали его как PDF/Word Document/MP3/что-то еще и предлагали пользователю сохранить таинственный файл на диск вместо". Это грязный хак, который ужасно ломается, когда клиент не выполняет «сохранение на диск».

...

И, наконец, вы неправильно кодируете имя файла, поэтому, если вы используете не-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
person Stefan Steiger    schedule 15.05.2013
comment
Итак, OP должен вытащить соответствующие биты из вашего кода? Я думаю, вы должны сделать это и объяснить, что отличается и почему. - person CodeCaster; 15.05.2013
comment
@CodeCaster: Ну, проще говоря, он забыл ms.Position = 0 и неправильно кодирует имя файла. - person Stefan Steiger; 15.05.2013

Вы используете очень длинный способ преобразования строки в байты.
Вы уверены, что вам нужны потоки? Почему бы просто не использовать кодировку?

Response.BinaryWrite(Encoding.UTF8.GetBytes("Something"))

person Dennis    schedule 15.05.2013