I'm in the process of creating a custom stream for an API endpoint in my app. The stream needs to have custom logic that I don't want to get 开发者_运维百科into, but suffice to say I can't use a built-in stream class.
I did the minimum necessary to implement a read-only stream (inheriting from System.IO.Stream) and I've verified that the System.IO.BinaryReader class can read from my stream:
Dim reader As New System.IO.BinaryReader(GenerateStream(business, logic))
Dim enc As New System.Text.ASCIIEncoding
Dim contents As String = enc.GetString(reader.ReadBytes(CType(reader.BaseStream.Length, Int32)))
The string "contents" contains the correct string for the entire stream.
However, I would like to be able allow the use of the System.IO.StreamReader class:
Dim reader As New System.IO.StreamReader(GenerateStream(business, logic), System.Text.Encoding.ASCII)
Dim contents As String = reader.ReadToEnd
but for whatever reason the ReadToEnd always returns the empty string.
Any ideas?
Here's the stream:
Public Overrides ReadOnly Property CanRead() As Boolean
Get
Return True
End Get
End Property
Public Overrides ReadOnly Property CanSeek() As Boolean
Get
Return False
End Get
End Property
Public Overrides ReadOnly Property CanWrite() As Boolean
Get
Return False
End Get
End Property
Public Overrides Sub Flush()
'this method intentionally left blank'
End Sub
Public Overrides ReadOnly Property Length() As Long
Get
Return 'some business logic'
End Get
End Property
Public Overrides Property Position() As Long
Get
Return bytePosition
End Get
Set(ByVal value As Long)
Throw New System.NotSupportedException
End Set
End Property
Public Overrides Function Read(ByVal buffer() As Byte, ByVal offset As Integer, ByVal count As Integer) As Integer
'I return 0 on an end of stream, otherwise the # of bytes successfully read.'
End Function
Public Overrides Function Seek(ByVal offset As Long, ByVal origin As System.IO.SeekOrigin) As Long
Throw New System.NotSupportedException
End Function
Public Overrides Sub SetLength(ByVal value As Long)
Throw New System.NotSupportedException()
End Sub
Public Overrides Sub Write(ByVal buffer() As Byte, ByVal offset As Integer, ByVal count As Integer)
Throw New System.NotSupportedException()
End Sub
Looking closely at the description of StreamReader.ReadToEnd
shows, quote:
If the initial position within the stream is unknown or the stream does not support seeking, the underlying Stream object also needs to be reinitialized.
It further goes on, saying:
To avoid such a situation and produce robust code you should use the Read method and store the read characters in a pre-allocated buffer.
But, looking under the hood, only Stream.Read is called, it seems, which should provide the output you need. But, you do know that StreamReader reads characters, it tries hard to interpret the data as a string. Not sure what happens if it cannot. What does the data consist of?
Update: Consider the following test code (sorry, C#, but use a converter to get your code), I did not include the non-implemented methods from the abstract class so as to not distract from the core. Just as a proof of concept, this seems to work with ASCII encoding and without the need to re-init or an implementation for Seek.
public class AsciiStream : Stream
{
private string dataStream = "abcdefghijklmnopqrstuvwxyz";
private long position = 0;
public override bool CanRead
{
get { return true; }
}
public override bool CanSeek
{
get { return false; }
}
public override bool CanWrite
{
get { return false; }
}
public override long Length
{
get { return dataStream.Length; }
}
public override long Position
{
get
{
return position;
}
set
{
position = value < this.Length ? value : this.Length;
}
}
public override int Read(byte[] buffer, int offset, int count)
{
long bufferPos = offset;
long max = this.position + count;
max = max < this.Length ? max : this.Length;
for (; this.position < max; this.position++)
{
buffer[bufferPos] = Convert.ToByte(this.dataStream[(int) this.position]);
bufferPos++;
}
return (int) bufferPos - offset;
}
}
// call the code like as follows:
StreamReader sReader = new StreamReader(new AsciiStream(), Encoding.ASCII);
string sToEnd = sReader.ReadToEnd();
Point being: the problem must lie in your actual Read-code or in the business logic you don't show us. The approach you've chosen, on itself, should simply work. Have you tried implementing, by hand, a "read-to-end"? Or with the BinaryReader
, reading past the end with ReadBytes(many)``?
Try doing this:
Dim reader As New System.IO.StreamReader(
GenerateStream(business, logic),
System.Text.Encoding.ASCII,
False
)
Dim contents As String = reader.ReadToEnd
Also make sure all the methods you don't want used will throw NotImplementedException
.
精彩评论