Partilhar via


Compor fluxos

Um armazenamento de backup é um meio de armazenamento, como um disco ou memória. Cada tipo de armazenamento de suporte implementa seu próprio fluxo como uma implementação da Stream classe.

Cada tipo de fluxo lê e grava bytes de e para seu armazenamento de backup fornecido. Os fluxos que se conectam a repositórios de backup são chamados de fluxos base. Os fluxos de base têm construtores com os parâmetros necessários para conectar o fluxo ao armazenamento de suporte. Por exemplo, FileStream tem construtores que especificam um parâmetro de modo de acesso, que determina se o arquivo é lido, gravado ou ambos.

O design das System.IO classes fornece composição de fluxo simplificada. Você pode anexar fluxos base a um ou mais fluxos de passagem que fornecem a funcionalidade desejada. Você pode anexar um leitor ou escritor ao final da cadeia, para que os tipos preferidos possam ser lidos ou escritos facilmente.

Pré-requisitos

Esses exemplos usam um arquivo de texto simples chamado data.txt. Este ficheiro deve conter algum texto.

Exemplo: criptografar e descriptografar dados de fluxo

O exemplo a seguir lê dados de um arquivo, criptografa-os e grava os dados criptografados em outro arquivo. A composição do fluxo é usada para transformar os dados usando uma cifra de deslocamento básica. Cada byte que passa pelo fluxo tem seu valor alterado em 80.

Aviso

A criptografia usada neste exemplo é básica e não segura. Ele não se destina a realmente criptografar dados para uso, mas é fornecido para demonstrar a alteração de dados por meio da composição do fluxo.

Leia os dados de origem para criptografia

O código a seguir lê o texto de um arquivo, transforma-o e, em seguida, grava-o em outro arquivo.

Gorjeta

Antes de revisar esse código, saiba que o CipherStream é um tipo definido pelo usuário. O código para essa classe é fornecido na seção de classe CipherStream.

void WriteShiftedFile()
{
    // Create the base streams for the input and output files
    using FileStream inputBaseStream = File.OpenRead("data.txt");
    using CipherStream encryptStream = CipherStream.CreateForRead(inputBaseStream);
    using FileStream outputBaseStream = File.Open("shifted.txt", FileMode.Create, FileAccess.Write);

    int intValue;

    // Read byte from inputBaseStream through the encryptStream (normal bytes into shifted bytes)
    while ((intValue = encryptStream.ReadByte()) != -1)
    {
        outputBaseStream.WriteByte((byte)intValue);
    }

    // Process is:
    //  (inputBaseStream -> encryptStream) -> outputBaseStream
}
Sub WriteShiftedFile()

    'Create the base streams for the input and output files
    Using inputBaseStream As FileStream = File.OpenRead("data.txt")
        Using encryptStream As CipherStream = CipherStream.CreateForRead(inputBaseStream)
            Using outputBaseStream As FileStream = File.Open("shifted.txt", FileMode.Create, FileAccess.Write)


                'Read byte from inputBaseStream through the encryptStream (normal bytes into shifted bytes)
                Dim intValue As Integer = encryptStream.ReadByte()
                While intValue <> -1

                    outputBaseStream.WriteByte(Convert.ToByte(intValue))

                    intValue = encryptStream.ReadByte()

                End While

            End Using
        End Using
    End Using

    'Process is:
    '  (inputBaseStream -> encryptStream) -> outputBaseStream
End Sub

Considere os seguintes aspetos sobre o código anterior:

  • Existem dois FileStream objetos:
    • O primeiro FileStream objeto (inputBaseStream variável) lê o conteúdo do arquivo data.txt. Este é o fluxo de dados de entrada .
    • O segundo FileStream objeto (outputBaseStream variável) grava dados de entrada no arquivo shifted.txt . Este é o fluxo de dados de saída .
  • O CipherStream objeto (encryptStream variável) encapsula o inputBaseStream, tornando inputBaseStream o fluxo base para encryptStream.

O fluxo de entrada poderia ser lido diretamente, gravando os dados no fluxo de saída, mas isso não transformaria os dados. Em vez disso, o wrapper de encryptStream fluxo de entrada é usado para ler os dados. À medida que os dados são lidos do encryptStream, ele extrai do inputBaseStream fluxo base, transforma-os e os devolve. Os dados retornados são gravados no outputBaseStream, que grava os dados no arquivo shifted.txt .

Leia os dados transformados para desencriptação

Este código reverte a encriptação executada pelo código anterior:

void ReadShiftedFile()
{
    int intValue;

    // Create the base streams for the input and output files
    using FileStream inputBaseStream = File.OpenRead("shifted.txt");
    using FileStream outputBaseStream = File.Open("unshifted.txt", FileMode.Create, FileAccess.Write);
    using CipherStream unencryptStream = CipherStream.CreateForWrite(outputBaseStream);

    // Read byte from inputBaseStream through the encryptStream (shifted bytes into normal bytes)
    while ((intValue = inputBaseStream.ReadByte()) != -1)
    {
        unencryptStream.WriteByte((byte)intValue);
    }

    // Process is:
    //  inputBaseStream -> (encryptStream -> outputBaseStream)
}
Sub ReadShiftedFile()

    'Create the base streams for the input and output files
    Using inputBaseStream As FileStream = File.OpenRead("shifted.txt")
        Using outputBaseStream As FileStream = File.Open("unshifted.txt", FileMode.Create, FileAccess.Write)
            Using unencryptStream As CipherStream = CipherStream.CreateForWrite(outputBaseStream)


                'Read byte from inputBaseStream through the encryptStream (shifted bytes into normal bytes)
                Dim intValue As Integer = inputBaseStream.ReadByte()
                While intValue <> -1

                    unencryptStream.WriteByte(Convert.ToByte(intValue))

                    intValue = inputBaseStream.ReadByte()

                End While

            End Using
        End Using
    End Using

End Sub

Considere os seguintes aspetos sobre o código anterior:

  • Existem dois FileStream objetos:
    • O primeiro FileStream objeto (inputBaseStream variável) lê o conteúdo do arquivo shifted.txt. Este é o fluxo de dados de entrada .
    • O segundo FileStream objeto (outputBaseStream variável) grava dados de entrada no arquivo unshifted.txt . Este é o fluxo de dados de saída .
  • O CipherStream objeto (unencryptStream variável) encapsula o outputBaseStream, tornando outputBaseStream o fluxo base para unencryptStream.

Aqui, o código é ligeiramente diferente do exemplo anterior. Em vez de envolver o fluxo de entrada, unencryptStream encapsula o fluxo de saída. À medida que os dados são lidos do fluxo de entrada, eles são enviados para o unencryptStream wrapper do fluxo de inputBaseStream saída. Quando unencryptStream recebe dados, transforma-os e, em seguida, grava-os outputBaseStream no fluxo de base. O outputBaseStream fluxo de saída grava os dados no arquivo unshifted.txt .

Validar os dados transformados

Os dois exemplos anteriores realizaram duas operações nos dados. Primeiro, o conteúdo do ficheiro data.txt foi encriptado e guardado no ficheiro shifted.txt . E, em segundo lugar, o conteúdo encriptado do ficheiro shifted.txt foi desencriptado e guardado no ficheiro unshifted.txt . Portanto, o arquivo data.txt e o arquivo unshifted.txt devem ser exatamente os mesmos. O código a seguir compara esses arquivos para igualdade:

bool IsShiftedFileValid()
{
    // Read the shifted file
    string originalText = File.ReadAllText("data.txt");

    // Read the shifted file
    string shiftedText = File.ReadAllText("unshifted.txt");

    // Check if the decrypted file is valid
    return shiftedText == originalText;
}
Function IsShiftedFileValid() As Boolean

    'Read the shifted file
    Dim originalText As String = File.ReadAllText("data.txt")

    'Read the shifted file
    Dim shiftedText As String = File.ReadAllText("unshifted.txt")

    'Check if the decrypted file is valid
    Return shiftedText = originalText

End Function

O código a seguir executa todo esse processo de encriptação-desencriptação:

// Read the contents of data.txt, encrypt it, and write it to shifted.txt
WriteShiftedFile();

// Read the contents of shifted.txt, decrypt it, and write it to unshifted.txt
ReadShiftedFile();

// Check if the decrypted file is valid
Console.WriteLine(IsShiftedFileValid()
                    ? "Decrypted file is valid"     // True
                    : "Decrypted file is invalid"   // False
                 );

// Output:
//   Decrypted file is valid
Sub Main(args As String())
    'Read the contents of data.txt, encrypt it, And write it to shifted.txt
    WriteShiftedFile()

    'Read the contents of shifted.txt, decrypt it, And write it to unshifted.txt
    ReadShiftedFile()

    'Check if the decrypted file Is valid
    Console.WriteLine(IIf(IsShiftedFileValid(),
                            "Decrypted file is valid",  ' True
                            "Decrypted file is invalid" ' False
                     ))
End Sub

Classe CipherStream

O trecho a seguir fornece a CipherStream classe, que usa uma cifra de deslocamento básica para criptografar e descriptografar bytes. Esta classe herda e suporta dados de Stream leitura ou gravação.

Aviso

A criptografia usada neste exemplo é básica e não segura. Ele não se destina a realmente criptografar dados para uso, mas é fornecido para demonstrar a alteração de dados por meio da composição do fluxo.

using System.IO;

public class CipherStream : Stream
{
    // WARNING: This is a simple encoding algorithm and should not be used in production code

    const byte ENCODING_OFFSET = 80;

    private bool _readable;
    private bool _writable;

    private Stream _wrappedBaseStream;

    public override bool CanRead => _readable;
    public override bool CanSeek => false;
    public override bool CanWrite => _writable;
    public override long Length => _wrappedBaseStream.Length;
    public override long Position
    {
        get => _wrappedBaseStream.Position;
        set => _wrappedBaseStream.Position = value;
    }

    public static CipherStream CreateForRead(Stream baseStream)
    {
        return new CipherStream(baseStream)
        {
            _readable = true,
            _writable = false
        };
    }

    public static CipherStream CreateForWrite(Stream baseStream)
    {
        return new CipherStream(baseStream)
        {
            _readable = false,
            _writable = true
        };
    }

    private CipherStream(Stream baseStream) =>
        _wrappedBaseStream = baseStream;

    public override int Read(byte[] buffer, int offset, int count)
    {
        if (!_readable) throw new NotSupportedException();
        if (count == 0) return 0;

        int returnCounter = 0;

        for (int i = 0; i < count; i++)
        {
            int value = _wrappedBaseStream.ReadByte();

            if (value == -1)
                return returnCounter;

            value += ENCODING_OFFSET;
            if (value > byte.MaxValue)
                value -= byte.MaxValue;

            buffer[i + offset] = Convert.ToByte(value);
            returnCounter++;
        }

        return returnCounter;
    }

    public override void Write(byte[] buffer, int offset, int count)
    {
        if (!_writable) throw new NotSupportedException();

        byte[] newBuffer = new byte[count];
        buffer.CopyTo(newBuffer, offset);

        for (int i = 0; i < count; i++)
        {
            int value = newBuffer[i];

            value -= ENCODING_OFFSET;

            if (value < 0)
                value = byte.MaxValue - value;

            newBuffer[i] = Convert.ToByte(value);
        }

        _wrappedBaseStream.Write(newBuffer, 0, count);
    }

    public override void Flush() => _wrappedBaseStream.Flush();
    public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException();
    public override void SetLength(long value) => throw new NotSupportedException();
}
Imports System.IO

Public Class CipherStream
    Inherits Stream

    Const ENCODING_OFFSET As Byte = 80

    Private _readable As Boolean = False
    Private _writable As Boolean = False

    Private _wrappedBaseStream As Stream

    Public Overrides ReadOnly Property CanRead As Boolean
        Get
            Return _readable
        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 _writable
        End Get
    End Property

    Public Overrides ReadOnly Property Length As Long
        Get
            Return _wrappedBaseStream.Length
        End Get
    End Property

    Public Overrides Property Position As Long
        Get
            Return _wrappedBaseStream.Position
        End Get
        Set(value As Long)
            _wrappedBaseStream.Position = value
        End Set
    End Property

    Public Shared Function CreateForRead(baseStream As Stream) As CipherStream
        Return New CipherStream(baseStream) With
        {
            ._readable = True,
            ._writable = False
        }
    End Function

    Public Shared Function CreateForWrite(baseStream As Stream) As CipherStream
        Return New CipherStream(baseStream) With
        {
            ._readable = False,
            ._writable = True
        }
    End Function

    Private Sub New(baseStream As Stream)
        _wrappedBaseStream = baseStream
    End Sub

    Public Overrides Function Read(buffer() As Byte, offset As Integer, count As Integer) As Integer

        If Not _readable Then Throw New NotSupportedException()
        If count = 0 Then Return 0

        Dim returnCounter As Integer = 0

        For i = 0 To count - 1

            Dim value As Integer = _wrappedBaseStream.ReadByte()

            If (value = -1) Then Return returnCounter

            value += ENCODING_OFFSET
            If value > Byte.MaxValue Then
                value -= Byte.MaxValue
            End If

            buffer(i + offset) = Convert.ToByte(value)
            returnCounter += 1

        Next

        Return returnCounter

    End Function

    Public Overrides Sub Write(buffer() As Byte, offset As Integer, count As Integer)
        If Not _writable Then Throw New NotSupportedException()

        Dim newBuffer(count) As Byte
        buffer.CopyTo(newBuffer, offset)

        For i = 0 To count - 1

            Dim value As Integer = newBuffer(i)

            value -= ENCODING_OFFSET

            If value < 0 Then
                value = Byte.MaxValue - value
            End If

            newBuffer(i) = Convert.ToByte(value)

        Next

        _wrappedBaseStream.Write(newBuffer, 0, count)

    End Sub


    Public Overrides Sub Flush()
        _wrappedBaseStream.Flush()
    End Sub

    Public Overrides Function Seek(offset As Long, origin As SeekOrigin) As Long
        Throw New NotSupportedException()
    End Function

    Public Overrides Sub SetLength(value As Long)
        Throw New NotSupportedException()
    End Sub

End Class

Consulte também