以下のサンプルコードを使用して、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();
次のエラーが表示されます。
指定された引数は有効な値の範囲外でした。
パラメーター名:オフセット
原因は何ですか?
データを配列にコピーするコードの時点で、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();
ここで論理的に何か間違ったことをしている。まず、MemoryStream
にテキストを書き込み、次に同じストリームに空の配列を書き込みます。ストリームのコンテンツをbytesInStream
配列にコピーしようとしていると思います。 memoryStream.ToArray()
を呼び出すことにより、この配列を作成できます。
または、 MemoryStream.CopyTo
を使用して、ストリームを 応答出力ストリーム に直接書き込むことにより、配列のコピーを回避できます。 BinaryWrite
呼び出しを次のように置き換えます。
memoryStream.Position = 0;
memoryStream.CopyTo(Response.OutputStream);
注:CopyTo
は現在の位置からコピーするため、ストリームを明示的に開始位置に配置します。
わかりました、実際の例を提供することで明らかに反対票を投じられるので、詳しく説明します。
まず、あなたはしません
textWriter.Flush()
テキストライターのコンテンツがメモリストリームにフラッシュされたことを期待します。
その後、あなたはしません
memoryStream.Position = 0
そして、メモリストリームが位置0から「書き込まれる」ことを期待します。
それからあなたは
memoryStream.Write(bytesInStream, 0, bytesInStream.Length);
しかし、あなたが実際に意味するのは
memoryStream.Read(bytesInStream, 0, CInt(memoryStream.Length))
また、読み取りが整数を使用している間、長さが長いことを見逃したため、そこで例外を取得できます。
したがって、これは「作業」に最小限に適合したコードです(私はそれを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
つまり
「私、Webサーバーは、このファイルが何であるかについてあなた(ブラウザ)に嘘をついて、それをPDF/Word Document/MP3/whateverとして扱い、不可解なファイルをディスクに保存するようユーザーに促します代わりに」。これは、クライアントが「ディスクに保存」しなかった場合に恐ろしく壊れる汚いハックです。
...
最後に、ファイル名を正しくエンコードしないため、ファイル名に非ASCII文字を使用すると、ファイル名が文字化けします。中国語またはロシア語でASCII文字セット。
ここに、私のajaxハンドラーの1つからの抜粋を示します。 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
文字列をバイトに変換するのに非常に長い方法を使用しています。
本当にストリームが必要ですか?エンコードを使用しないのはなぜですか?
Response.BinaryWrite(Encoding.UTF8.GetBytes("Something"))