Question

Is it possible to take all files and folders in a directory and pack them into a single package file, so that I may transfer this package over network and then unpack all files and folders from the package?

I tried looking into ZIP files with C#, because I'm aiming for the same idea, but the actual methods for it only comes with .NET 3.5 (I believe), I also want the program to be very lightweight, meaning I don't want external modules lying around that has to be taken with if I wish to unzip/unpack a single file.

How can I accomplish this?

Was it helpful?

Solution

Just use a BinaryWriter/Reader and your own format. Something like this:

using (var fs = File.Create(...))
using (var bw = new BinaryWriter(fs))
{
    foreach (var file in Directory.GetFiles(...))
    {
        bw.Write(true); // means that a file will follow
        bw.Write(Path.GetFileName(file));
        var data = File.ReadAllBytes(file);
        bw.Write(data.Length);
        bw.Write(data);
    }
    bw.Write(false); // means end of file
}

So basically you write a bool that means whether there is a next file, the name and contents of each file, one after the other. Reading is the exact opposite. BinaryWriter/Reader take care of everything (it knows how long each string and byte array is, you will read back exactly what you wrote).

What this solution lacks: not an industry standard (but quite simple), doesn't store any additional metadata (you can add creation time, etc.), doesn't use a checksum (you can add an SHA1 hash after the contents), doesn't use compression (you said you don't need it), doesn't handle big files well (the problematic part is that it reads an entire file into a byte array and writes that, should work pretty well under 100 MB), doesn't handle multi-level directory hierarchies (can be added of course).

EDIT: The BinaryR/W know about string lengths, but not about byte array lengths. I added a length field before the byte array so that it can be read back exactly as it was written.

OTHER TIPS

Take a look at ziplib, it's free, open source and can be used in all .NET versions: http://www.icsharpcode.net/opensource/sharpziplib/

What i suggest you it's to consider the advantages taken by using an external library so you can forget a lot of troubles. A zip complex class could be a huge deal. Take a look at that: http://dotnetzip.codeplex.com/ it's simple, stable and lightweigth.

By the way if you totally don'want external libraries and data compression isn't mandatory for your project you can manage it in a sort like this (please, consider it as a sample written in lees than an hour ;-) ):

usage:

//to pack
Packer.SimplePack sp = new Packer.SimplePack(@"c:\filename.pack");
sp.PackFolderContent(@"c:\yourfolder");
sp.Save();

//to unpack
Packer.SimplePack sp = new Packer.SimplePack(@"c:\filename.pack");
sp.Open();

Here is SimplePack:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Packer
{
    public class SimplePack
    {
        public class Header
        {
            public Int32 TotalEntries { get; set; }
            public Int64[] EntriesSize
            {
                get
                {
                    return EntriesSizeList.ToArray();
                }
            }
            private List<Int64> EntriesSizeList { get; set; }

            public Header()
            {
                TotalEntries = 0;
                EntriesSizeList = new List<Int64>();
            }

            public void AddEntrySize(Int64 newSize)
            {
                EntriesSizeList.Add(newSize);
            }
        }

        public class Item
        {
            public Byte[] RawData { get; set; }
            public String Name { get; set; }
            public String RelativeUri { get; set; }

            public Int64 ItemSize
            {
                get
                {
                    Int64 retVal = 4; //Name.Lenght;
                    retVal += Name.Length;
                    retVal += 4; //RelativeUri.Length
                    retVal += RelativeUri.Length;
                    retVal += RawData.Length;

                    return retVal;
                }
            }

            public Byte[] SerializedData
            {
                get
                {
                    List<Byte> retVal = new List<Byte>();
                    retVal.AddRange(BitConverter.GetBytes(Name.Length));
                    retVal.AddRange(Encoding.Default.GetBytes(Name));
                    retVal.AddRange(BitConverter.GetBytes(RelativeUri.Length));
                    retVal.AddRange(Encoding.Default.GetBytes(RelativeUri));
                    retVal.AddRange(RawData);
                    return retVal.ToArray();
                }
            }

            public Item()
            {
                RawData = new Byte[0];
                Name = String.Empty;
                RelativeUri = String.Empty;
            }
            public Item(Byte[] serializedItem)
            {
                Int32 cursor = 0;
                Int32 nl = BitConverter.ToInt32(serializedItem, cursor);
                cursor += 4;
                Name = Encoding.Default.GetString(serializedItem, cursor, nl);
                cursor += nl;
                Int32 rl = BitConverter.ToInt32(serializedItem, cursor);
                cursor += 4;
                RelativeUri = Encoding.Default.GetString(serializedItem, cursor, rl);
                cursor += rl;
                RawData = new Byte[serializedItem.Length - cursor];
                for (int i = cursor; i < serializedItem.Length; i++)
                {
                    RawData[i - cursor] = serializedItem[cursor];
                }
            }
        }

        public FileInfo PackedFile { get; private set; }
        public List<Item> Data { get; private set; }

        public Header FileHeaderDefinition { get; private set; }

        public SimplePack(String fileName)
        {
            PackedFile = new FileInfo(fileName);
            FileHeaderDefinition = new Header();
            Data = new List<Item>();
        }

        public Boolean PackFolderContent(String folderFullName)
        {
            Boolean retVal = false;

            DirectoryInfo di = new DirectoryInfo(folderFullName);

            //Think about setting up strong checks and errors trapping

            if (di.Exists)
            {
                FileInfo[] files = di.GetFiles("*", SearchOption.AllDirectories);
                foreach (FileInfo fi in files)
                {
                    Item it = setItem(fi, di.FullName);
                    if (it != null)
                    {
                        Data.Add(it);
                        FileHeaderDefinition.TotalEntries++;
                        FileHeaderDefinition.AddEntrySize(it.ItemSize);
                    }
                }
            }
            //althoug it isn't checked
            retVal = true;

            return retVal;
        }

        private Item setItem(FileInfo sourceFile, String packedRoot)
        {
            if (sourceFile.Exists)
            {
                Item retVal = new Item();
                retVal.Name = sourceFile.Name;
                retVal.RelativeUri = sourceFile.FullName.Substring(packedRoot.Length).Replace("\\", "/");
                retVal.RawData = File.ReadAllBytes(sourceFile.FullName);
                return retVal;
            }
            else
            {
                return null;
            }
        }

        public void Save()
        {
            if (PackedFile.Exists)
            {
                PackedFile.Delete();
                System.Threading.Thread.Sleep(100);
            }
            using (FileStream fs = new FileStream(PackedFile.FullName, FileMode.CreateNew, FileAccess.Write))
            {
                //Writing Header
                //4 bytes
                fs.Write(BitConverter.GetBytes(FileHeaderDefinition.TotalEntries), 0, 4);
                //8 bytes foreach size
                foreach (Int64 size in FileHeaderDefinition.EntriesSize)
                {
                    fs.Write(BitConverter.GetBytes(size), 0, 8);
                }
                foreach (Item it in Data)
                {
                    fs.Write(it.SerializedData, 0, it.SerializedData.Length);
                }

                fs.Close();
            }
        }

        public void Open()
        {
            if (PackedFile.Exists)
            {
                using (FileStream fs = new FileStream(PackedFile.FullName, FileMode.Open, FileAccess.Read))
                {
                    Byte[] readBuffer = new Byte[4];
                    fs.Read(readBuffer, 0, readBuffer.Length);
                    FileHeaderDefinition.TotalEntries = BitConverter.ToInt32(readBuffer, 0);
                    for (Int32 i = 0; i < FileHeaderDefinition.TotalEntries; i++)
                    {
                        readBuffer = new Byte[8];
                        fs.Read(readBuffer, 0, readBuffer.Length);
                        FileHeaderDefinition.AddEntrySize(BitConverter.ToInt64(readBuffer, 0));
                    }

                    foreach (Int64 size in FileHeaderDefinition.EntriesSize)
                    {
                        readBuffer = new Byte[size];
                        fs.Read(readBuffer, 0, readBuffer.Length);
                        Data.Add(new Item(readBuffer));
                    }

                    fs.Close();
                }
            }
        }




    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top