27

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?

JohnLBevan
  • 22,735
  • 13
  • 96
  • 178
Cheeso
  • 189,189
  • 101
  • 473
  • 713

6 Answers6

36

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.

SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
  • 1
    Looks easy. Can you set RFC 2045 line lengths on that stream? – Cheeso Mar 26 '10 at 18:02
  • You can call `Convert.ToBase64String(bytes, Base64FormattingOptions.InsertLineBreaks)` However, `ToBase64Transform` does not expose that option. – SLaks Mar 26 '10 at 18:03
  • ok that's odd. So if I wanna create a base64 doc for MIME-attach purposes (a la RFC 2045), then I can't used that, but if I convert FROM a base64 doc, it would work? – Cheeso Mar 28 '10 at 22:49
6

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);
sombra
  • 136
  • 2
  • 3
1

This should do what you are looking for:

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

0

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;
}
Jerameel Resco
  • 3,409
  • 7
  • 23
  • 30
Mikos
  • 8,455
  • 10
  • 41
  • 72
  • 3
    Useful, but in my case, I don't want to use the ToBase64String. It's not a stream and is likely to not work very well with large data. – Cheeso Mar 26 '10 at 18:03
-1

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

eli
  • 156
  • 5
  • 15
  • 5
    This does not completely answer the original question: that class is marked **internal** and you can't just copy the source code because it is *for reference* only. – JBert Aug 17 '17 at 16:12
  • You are right, I didn't think you can't copy the source. – eli Aug 18 '17 at 08:16
-2

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);
                }
            }
        }
    }
}
Mason G. Zhwiti
  • 6,444
  • 11
  • 61
  • 97
Cheeso
  • 189,189
  • 101
  • 473
  • 713
  • 1
    You don't need a custom stream class; you can just use a `CryptoStream`. See my answer. – SLaks Mar 26 '10 at 17:57
  • 2
    yes, but I couldn't figure out how to do RFC2045-compliant line lengths with the builtin class. So I *did* need a custom class, as far as I am aware. – Cheeso Apr 04 '11 at 17:54
  • @Cheeso how Can i decompress the stream outputed from your code snippet? – John Dec 09 '11 at 14:17
  • 1
    Well you'd just run the content through a compressor. But compressing a base64 string produced from compression seems like the wrong idea. The original content is compressed, then it is base64 encoded. Compressing the output would have the result of removing the benefits of base64 encoding. Also it likely wouldn't have any good result, since the content is already compressed therefore the base64 encoding of said content will be mostly incompressible. – Cheeso Mar 10 '12 at 16:58
  • 3
    @Cheeso, I'm interested in taking a look at your Base64Stream, but that winisp.net link appears to be dead. Do you have it anywhere else? – Eric Pohl Dec 20 '13 at 13:48
  • 2
    Dead links. Please don't post links, post the code. – tjmoore Sep 02 '16 at 11:18