Visual Studio 拡張と Roslyn インクリメンタルジェネレーターを用いて、Base64 変換ツールを作成します。
VS 拡張バージョン
以下のコードでは、EnvDTE80 NuGet パッケージが必要です。
using EnvDTE;
using EnvDTE80;
using System;
using System.Collections.Generic;
using System.IO;
namespace base64Tool
{
public static class MimeTypeConverter
{
public static string ConvertExtToMimeType(string ext)
{
var mappings = new Dictionary<string, string>
{
{ ".jpg", "image/jpeg" },
{ ".jpeg", "image/jpeg" },
{ ".png", "image/png" },
{ ".gif", "image/gif" }
};
return mappings.TryGetValue(ext.ToLower(), out var mimeType) ? mimeType : string.Empty;
}
public static string GetFileMimeType(string filePath)
{
var ext = Path.GetExtension(filePath);
return ConvertExtToMimeType(ext);
}
}
public static class Base64Encoder
{
public static string EncodeFileToDataURL(string filePath)
{
var mimeType = MimeTypeConverter.GetFileMimeType(filePath);
var bytes = File.ReadAllBytes(filePath);
var encodedContent = Convert.ToBase64String(bytes);
return CreateDataURI(mimeType, encodedContent);
}
private static string CreateDataURI(string mimeType, string encodedContent)
{
return !string.IsNullOrWhiteSpace(mimeType)
? $"data:{mimeType};base64,{encodedContent}"
: encodedContent;
}
}
internal class Program
{
static void Main()
{
var dte = Marshal.GetActiveObject("VisualStudio.DTE") as DTE2;
var selectedItem = dte.ToolWindows.SolutionExplorer.SelectedItems.Item(1);
var projectItem = (ProjectItem)selectedItem.Object;
var filePath = projectItem.FileNames[0];
var encodedContent = Base64Encoder.EncodeFileToDataURL(filePath);
var outputFilePath = GenerateOutputPath(filePath);
File.WriteAllText(outputFilePath, encodedContent);
System.Diagnostics.Process.Start(outputFilePath);
}
private static string GenerateOutputPath(string sourcePath)
{
var directory = Path.GetDirectoryName(sourcePath);
var fileNameWithoutExt = Path.GetFileNameWithoutExtension(sourcePath);
return Path.Combine(directory, $"{fileNameWithoutExt}_base64.txt");
}
}
}
Roslyn インクリメンタルジェネレーター
以下は、Roslyn ジェネレーターでの実装例です。
using Microsoft.CodeAnalysis;
using System.Linq;
namespace ResourceBase64GeneratorApp
{
public static class MimeTypeMapper
{
public static string MapExtToMimeType(string ext)
{
var mimeMap = new Dictionary<string, string>
{
{ ".jpg", "image/jpeg" },
{ ".jpeg", "image/jpeg" },
{ ".png", "image/png" },
{ ".gif", "image/gif" }
};
return mimeMap.TryGetValue(ext.ToLower(), out var type) ? type : string.Empty;
}
public static string FetchFileMimeType(string path)
{
var extension = Path.GetExtension(path);
return MapExtToMimeType(extension);
}
}
public static class DataURIEncoder
{
public static string EncodeFile(string filePath)
{
var mimeType = MimeTypeMapper.FetchFileMimeType(filePath);
var fileBytes = File.ReadAllBytes(filePath);
var base64Str = Convert.ToBase64String(fileBytes);
return ConstructDataURI(mimeType, base64Str);
}
private static string ConstructDataURI(string type, string base64)
{
return !string.IsNullOrWhiteSpace(type)
? $"data:{type};base64,{base64}"
: base64;
}
}
[Generator]
public class Base64ResourceGen : IIncrementalGenerator
{
public void Initialize(IncrementalGeneratorInitializationContext ctx)
{
var files = ctx.AdditionalTextsProvider;
ctx.RegisterSourceOutput(files, (ctxOut, file) =>
{
var projPath = RetrieveProjectPath(file.Path);
WriteCode(ctxOut, projPath, file.Path);
});
}
private static void WriteCode(SourceProductionContext ctx, string projPath, string resourcePath)
{
ExtractNamespaceAndConstName(projPath, resourcePath, out var ns, out var constName);
var dataURI = DataURIEncoder.EncodeFile(resourcePath);
var code = $@"using System;
namespace {ns}
{{
public static partial class Base64Resources
{{
public const string {constName} = ""{dataURI}"";
}}
}}
";
var genFileName = $"{constName}_{ns.Replace(".", "_")}.cs";
ctx.AddSource(genFileName, code);
}
private static void ExtractNamespaceAndConstName(string projPath, string resPath, out string ns, out string constName)
{
var rootDir = Path.GetDirectoryName(projPath);
var resDir = Path.GetDirectoryName(resPath);
var subNsPart = resDir.Substring(rootDir.Length).TrimStart(Path.DirectorySeparatorChar);
var rootNs = Path.GetFileNameWithoutExtension(projPath);
var subNsTokens = subNsPart.Split(Path.DirectorySeparatorChar, StringSplitOptions.RemoveEmptyEntries);
var namespaces = new List<string> { rootNs };
namespaces.AddRange(subNsTokens);
ns = string.Join(".", namespaces);
constName = Path.GetFileNameWithoutExtension(resPath);
}
private static string RetrieveProjectPath(string origPath)
{
var dir = Path.GetDirectoryName(origPath);
for (int depth = 0; depth < 4; depth++)
{
if (HasCsProj(dir)) return FindCsProjInFolder(dir);
dir = Path.GetDirectoryName(dir);
}
return string.Empty;
}
private static bool HasCsProj(string folder)
{
return Directory.GetFiles(folder, "*.csproj").Any();
}
private static string FindCsProjInFolder(string folder)
{
return Directory.GetFiles(folder, "*.csproj").FirstOrDefault();
}
}
}