Create A Resource File From File System Directory

We have a collection of files in a nested directory layout that we need to distribute as resources, so I wrote this small class to do it.

The ultimate use of the resource file that is so we can distribute this relatively large collection of small files in a single unit and allow the web application to grab the content via a handler. One could just use Visual Studio but it's editor is too slow when you get a bunch of file based resources.

using System;
using System.Text;
using System.Resources;
using System.IO;

public class ArchiveToResx
{
    public void Create(string dir, string resx)
    {
        m_dir = dir;
        m_resf = resx;
        m_resx = new ResXResourceWriter(m_resf);
        DoDirectory(new DirectoryInfo(c.m_dir));
        m_resx.AddResource("lut", (object)c.m_lut.ToString());
        m_resx.Generate();
        m_resx.Close();
    }

    // Traverse the directory and for each file create and entry in the resource
    // file and increment a counter. Save the counter value and the name in a 
    // string that will be saved later to act as a look up table.
    public void DoDirectory(DirectoryInfo di)
    {
        foreach (System.IO.FileInfo f in di.GetFiles())
        {
            using (FileStream fs = f.OpenRead())
            {
                byte[] contents = new byte[f.Length];
                fs.Read(contents, 0, contents.Length);
                string key = f.FullName.Replace(m_dir, string.Empty);
                key = key.Replace("\\", "/");
                if (key.StartsWith("/")) key = key.Substring(1);
                m_resx.AddResource("f" + m_cnt.ToString(), contents);
                if (m_lut.Length > 0) m_lut.Append(m_sep);
                m_lut.Append("f").Append(m_cnt).Append(m_sep).Append(key);
                m_cnt++;
            }
        }
        foreach (System.IO.DirectoryInfo d in di.GetDirectories())
        {
            DoDirectory(d);
        }
    }

    private int m_cnt = 1;
    private StringBuilder m_lut = new StringBuilder();
    private string m_sep = "|";
    private string m_dir;
    private string m_resf;
    private ResXResourceWriter m_resx;

    public static void Main()
    {
        MyClass c = new MyClass();
        c.Create(@"C:\temp\directory", @"C:\temp\directory.resx");
    }
}

After the resx file is created, I just drop it into Visual Studio. Here's an IHttpHandler that shows how the resource file is expanded and used.

using System;
using System.Collections.Generic;
using System.Web;

public class MyPathHandler : IHttpHandler
{
    public void ProcessRequest(HttpContext context)
    {
        string[] lut = MyResources.lut.Split('|');
        Dictionary dict = new Dictionary();
        for (int i = 0; i < lut.Length; i += 2)
        {
            dict.Add(lut[i + 1], lut[i]);
        }

        // Strip the part of the path that we use to get this handler called.
        string physicalPath = context.Request.Path.Replace("/Path/", string.Empty);
        if (dict.ContainsKey(physicalPath))
        {
            byte[] body = GetFile(dict[physicalPath]);
            context.Response.BinaryWrite(body);
            context.Response.Flush();
            return;
        }
        context.Response.WriteFile(context.Server.MapPath(context.Request.Path));
    }

    public bool IsReusable
    {
        get
        {
            return true;
        }
    }

    private byte[] GetFile(string id)
    {
        return (byte[])MyResources.ResourceManager.GetObject(id,
          MyResources.Culture);
    }
}

About this entry