45

I can't seem to find a more efficient way to "copy" an embedded resource to disk, than the following:

using (BinaryReader reader = new BinaryReader(
    assembly.GetManifestResourceStream(@"Namespace.Resources.File.ext")))
{
    using (BinaryWriter writer
        = new BinaryWriter(new FileStream(path, FileMode.Create)))
    {
        long bytesLeft = reader.BaseStream.Length;
        while (bytesLeft > 0)
        {
            // 65535L is < Int32.MaxValue, so no need to test for overflow
            byte[] chunk = reader.ReadBytes((int)Math.Min(bytesLeft, 65536L));
            writer.Write(chunk);

            bytesLeft -= chunk.Length;
        }
    }
}

There appears to be no more direct way to do the copy, unless I'm missing something...

leppie
  • 115,091
  • 17
  • 196
  • 297
user7116
  • 63,008
  • 17
  • 141
  • 172

6 Answers6

68

I'm not sure why you're using BinaryReader/BinaryWriter at all. Personally I'd start off with a useful utility method:

public static void CopyStream(Stream input, Stream output)
{
    // Insert null checking here for production
    byte[] buffer = new byte[8192];

    int bytesRead;
    while ((bytesRead = input.Read(buffer, 0, buffer.Length)) > 0)
    {
        output.Write(buffer, 0, bytesRead);
    }
}

then call it:

using (Stream input = assembly.GetManifestResourceStream(resourceName))
using (Stream output = File.Create(path))
{
    CopyStream(input, output);
}

You can change the buffer size of course, or have it as a parameter to the method - but the main point is that this is simpler code. Is it more efficient? Nope. Are you sure you really need this code to be more efficient? Do you actually have hundreds of megabytes you need to write out to disk?

I find I rarely need code to be ultra-efficient, but I almost always need it to be simple. The sort of difference in performance that you might see between this and a "clever" approach (if one is even available) isn't likely to be a complexity-changing effect (e.g. O(n) to O(log n)) - and that's the type of performance gain which really can be worth chasing.

EDIT: As noted in comments, .NET 4.0 has Stream.CopyTo so you don't need to code this up yourself.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Glorious, I guess I've fallen prey to ignoring the Stream class. Poor, poor Stream. – user7116 May 14 '09 at 16:14
  • The file in question is between 5-10MB, so it is negligible in terms of speed. I was looking for something that was simple/concise (since simple/concise tends to mean efficient). – user7116 May 14 '09 at 16:17
  • I tried changing to CopyTo(), and ran into a lot of "The process cannot access the file because it is being used by another process." errors from ASP.NET (cause the Stream is still open). I went back to using the usings to clear that up. – eduncan911 Mar 11 '11 at 00:41
  • 1
    @eduncan911: Using CopyTo doesn't change whether or not you need using statements. It just changes how the process of copying itself happens, while the streams are open. – Jon Skeet Mar 11 '11 at 06:22
66

If the resource (file) is binary.

File.WriteAllBytes("C:\ResourceName", Resources.ResourceName);

And if the resource (file) is text.

 File.WriteAllText("C:\ResourceName", Resources.ResourceName);
djv
  • 15,168
  • 7
  • 48
  • 72
KoalaBear
  • 2,755
  • 2
  • 25
  • 29
  • 4
    This is the only reasonable answer! Everyone else is providing absurd constructs with multiple streams, buffers and over 10 lines when this one line does the same. – ASA Mar 03 '14 at 14:48
  • This approach looks at the very least incomplete. The .NET 4.0 documentation for [File.WriteAllBytes](https://msdn.microsoft.com/en-us/library/system.io.file.writeallbytes%28v=vs.100%29.aspx) indicates that the second argument is a byte array whose content is to be written to the file. And the documentation for [File.WriteAllText](https://msdn.microsoft.com/en-us/library/ms143375%28v=vs.100%29.aspx) indicates that the second argument is the string to write to the file. So, specifying `Resources.ResourceName` as the second argument doesn't really make sense. – DavidRR Nov 13 '15 at 14:43
  • If `Resources.ResourceName` is binary the type of `Resources.ResourceName` is `Byte[]` as required, and it will work as expected. Does this make it complete/clear for you? – KoalaBear Nov 13 '15 at 18:56
  • Much better answer. One line! – Peter Gordon Feb 20 '16 at 19:43
  • I am feeling a bit dense since others have clearly used this solution. When I run File.WriteAllText(output_file, resource_name), I of course end up with a file containing the name of the resource. Are you using the System.Resources namespace? If so, how do you reference a specific embedded assembly resource through this namespace by name? Is this perhaps a VB thing rather than C#? – michael Jun 11 '16 at 18:54
  • When you look at your Resources, they will all have names. Use this name in the second parameter. Maybe you need to type "Properties.Resources.ResourceName" instead if you dont have the Properties using added. Then it will read the resource and write it to file. – KoalaBear Jun 12 '16 at 18:47
  • + for WriteAllText – M at Jul 28 '16 at 08:47
  • I loved this solution. – Haider Ali Wajihi Dec 15 '16 at 05:31
22

I actually ended up using this single line: Assembly.GetExecutingAssembly().GetManifestResourceStream("[Project].[File]").CopyTo(New FileStream(FileLocation, FileMode.Create)). Of course, this is for .Net 4.0

Update: I found that the line above might keep a file locked such that SQLite reports that the database is read-only. Therefore I ended up with the following:

Using newFile As Stream = New FileStream(FileLocation, FileMode.Create)
    Assembly.GetExecutingAssembly().GetManifestResourceStream("[Project].[File]").CopyTo(newFile)
End Using
cjbarth
  • 4,189
  • 6
  • 43
  • 62
  • 3
    +1, definitely the way to go .Net 4.0+. I'd also note a quick way to create `FileStream`s is with the static methods on the `File` object, like [`File.Create()`](http://msdn.microsoft.com/en-us/library/system.io.file.create.aspx). – user7116 Apr 14 '12 at 01:09
2

Personally I would do it this way:

using (BinaryReader reader = new BinaryReader(
    assembly.GetManifestResourceStream(@"Namespace.Resources.File.ext")))
{
    using (BinaryWriter writer
        = new BinaryWriter(new FileStream(path, FileMode.Create)))
    {
        byte[] buffer = new byte[64 * 1024];
        int numread = reader.Read(buffer,0,buffer.Length);

        while (numread > 0)
        {
            writer.Write(buffer,0,numread);
            numread = reader.Read(buffer,0,buffer.Length);
        }

        writer.Flush();
    }
}
Lloyd
  • 29,197
  • 4
  • 84
  • 98
  • I like that minus the flush, but excluding a more direct way, I think I'm going to take yours as the answer. – user7116 May 14 '09 at 16:08
2

You will have to write a loop, if that's your question. But you could do without the reader and writer since the basic Stream already deals with byte[] data.

This is about as compact as I can get:

using (Stream inStream = File.OpenRead(inputFile))
using (Stream outStream = File.OpenWrite(outputFile))
{
    int read;
    byte[] buffer = new byte[64 * 1024];

    while ((read = inStream.Read(buffer, 0, buffer.Length)) > 0)
    {
        outStream.Write(buffer, 0, read);
    }
}
H H
  • 263,252
  • 30
  • 330
  • 514
0

This Worked for me:

    using (MemoryStream input = new MemoryStream(Properties.Resources.*RESOURCE_NAME*))
    using (Stream output = File.Create(databasePath)){


                    input.CopyTo(output)
    }
arcalo
  • 1