开发者

Is there a Base64Stream for .NET?

If I want to produce a Base64-encoded output, how would I do开发者_运维问答 that in .NET?

I know that since .NET 2.0, there is the ICryptoTransform interface, and the ToBase64Transform() and FromBase64Transform() implementations of that interface.

But those classes are embedded into the System.Security namespace, and require the use of a TransformBlock, TransformFinalBlock, and so on.

Is there an easier way to base64 encode a stream of data in .NET?


If you want a stream that converts to Base64, you can put a ToBase64Transform into a CryptoStream:

new CryptoStream(stream, new ToBase64Transform(), CryptoStreamMode.Write)

If you just want to convert a single byte array to Base64, you can simply call Convert.ToBase64String(bytes).

In both cases, you can replace the word To with From.


With the simple answer from @SLaks I have written a simple solution:

/// <summary>
///     Encodes the specified input stream into the specified output stream.
/// </summary>
/// <param name="inputStream">
///     The input stream.
/// </param>
/// <param name="outputStream">
///     The output stream.
/// </param>
/// <param name="lineLength">
///     The length of lines.
/// </param>
/// <param name="dispose">
///     true to release all resources used by the input and output <see cref="Stream"/>;
///     otherwise, false.
/// </param>
/// <exception cref="ArgumentNullException">
///     inputStream or outputStream is null.
/// </exception>
/// <exception cref="ArgumentException">
///     inputStream or outputStream is invalid.
/// </exception>
/// <exception cref="NotSupportedException">
///     inputStream is not readable -or- outputStream is not writable.
/// </exception>
/// <exception cref="IOException">
///     An I/O error occured, such as the specified file cannot be found.
/// </exception>
/// <exception cref="ObjectDisposedException">
///     Methods were called after the inputStream or outputStream was closed.
/// </exception>
public static void EncodeStream(Stream inputStream, Stream outputStream, int lineLength = 0, bool dispose = false)
{
    if (inputStream == null)
        throw new ArgumentNullException(nameof(inputStream));
    if (outputStream == null)
        throw new ArgumentNullException(nameof(outputStream));
    var si = inputStream;
    var so = outputStream;
    try
    {
        int i;
        var cs = new CryptoStream(si, new ToBase64Transform(), CryptoStreamMode.Read);
        var ba = new byte[lineLength < 1 ? 4096 : lineLength];
        var sep = new byte[] { 0xd, 0xa };
        while ((i = cs.Read(ba, 0, ba.Length)) > 0)
        {
            so.Write(ba, 0, i);
            if (lineLength < 1 || i < ba.Length)
                continue;
            so.Write(sep, 0, sep.Length);
        }
    }
    finally
    {
        if (dispose)
        {
            si.Dispose();
            so.Dispose();
        }
    }
}

/// <summary>
///     Decodes the specified input stream into the specified output stream.
/// </summary>
/// <param name="inputStream">
///     The input stream.
/// </param>
/// <param name="outputStream">
///     The output stream.
/// </param>
/// <param name="dispose">
///     true to release all resources used by the input and output <see cref="Stream"/>;
///     otherwise, false.
/// </param>
/// <exception cref="ArgumentNullException">
///     inputStream or outputStream is null.
/// </exception>
/// <exception cref="ArgumentException">
///     inputStream or outputStream is invalid.
/// </exception>
/// <exception cref="NotSupportedException">
///     inputStream is not readable -or- outputStream is not writable.
/// </exception>
/// <exception cref="IOException">
///     An I/O error occured, such as the specified file cannot be found.
/// </exception>
/// <exception cref="ObjectDisposedException">
///     Methods were called after the inputStream or outputStream was closed.
/// </exception>
public static void DecodeStream(Stream inputStream, Stream outputStream, bool dispose = false)
{
    if (inputStream == null)
        throw new ArgumentNullException(nameof(inputStream));
    if (outputStream == null)
        throw new ArgumentNullException(nameof(outputStream));
    var si = inputStream;
    var so = outputStream;
    try
    {
        var bai = new byte[4096];
        var bao = new byte[bai.Length];
        using (var fbt = new FromBase64Transform())
        {
            int i;
            while ((i = si.Read(bai, 0, bai.Length)) > 0)
            {
                i = fbt.TransformBlock(bai, 0, i, bao, 0);
                so.Write(bao, 0, i);
            }
        }
    }
    finally
    {
        if (dispose)
        {
            si.Dispose();
            so.Dispose();
        }
    }
}

I only use CryptoStream to encode because I've found it has problems decoding Base64 hahes with line breaks, while FromBase64Transform can do that easily without CryptoStream. I hope the rest is clear enough that I do not have to explain anything.

Example:

const int lineLength = 76;
const string sourcePath = @"C:\test\file-to-encode.example";
const string encodedPath = @"C:\test\encoded-example.base64";
const string decodedPath = @"C:\test\decoded-base64.example";

// encoding
using(var fsi = new FileStream(sourcePath, FileMode.Open))
    using (var fso = new FileStream(encodedPath, FileMode.Create))
        EncodeStream(fsi, fso, lineLength);

// decoding
using(var fsi = new FileStream(encodedPath, FileMode.Open))
    using (var fso = new FileStream(decodedPath, FileMode.Create))
        DecodeStream(fsi, fso);


This should do what you are looking for:

http://mews.codeplex.com/SourceControl/changeset/view/52969#392973


System.Convert provides that, here is a code sample that might help

private string EncodeBase64(string toEncode)
{
  byte[] toEncodeAsBytes = System.Text.ASCIIEncoding.ASCII.GetBytes(toEncode);
  string returnValue = System.Convert.ToBase64String(toEncodeAsBytes);
  return returnValue;
}


It looks like there is a Base64Stream class. Maybe it'll help future readers.

http://referencesource.microsoft.com/#system/net/System/Net/mail/Base64Stream.cs


The CryptoStream does not do RFC2045 line endings. Therefore that won't work for me.
The ToBase64String is unacceptable because it's not a streaming approach. I don't want to hold all the data in memory at one time.

Therefore I needed alternatives.

Richard Grimes posted one here:
http://www.grimes.nildram.co.uk/workshops/encodedStream.htm

Rather than use that, because of licensing and feature needs, I wrote an independent implementation, available here:
http://cheeso.members.winisp.net/srcview.aspx?dir=streams&file=Base64Stream.cs

It is licensed under the MS-PL.

To use this to compress then base64-encode a file, do this:

byte[] working= new byte[1024];
int n;
using (Stream input = File.OpenRead(fileToCompress))
{
    using (Stream output = new FileStream("file.deflated.b64"))
    {
        using (var b64 = new Base64Stream(output, Base64Stream.Mode.Encode))
        {
            b64.Rfc2045Compliant = true; // OutputLineLength = 76;

            using (var compressor = new DeflateStream(b64, CompressionMode.Compress, true))
            {
                while ((n = input.Read(working, 0, working.Length)) != 0)
                {
                    compressor.Write(working, 0, n);
                }
            }
        }
    }
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜