VS 插件和 Roslyn 增量生成器生成 Base64 预处理工具
VS 插件和 Roslyn 增量生成器生成 Base64 预处理工具
1. 前言
总之受人所托,具体编写过程可见 https://www.bilibili.com/video/BV1Gx4y1t7it
2. VS 插件版本
需要引入 NuGet 包 EnvDTE80

using EnvDTE;
using EnvDTE80;
using System;
using System.Collections;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
namespace base64it
{
public static class ExtensionToMIMEConverter
{
// .jpg => image/jpeg
// .jpeg => image/jpeg
public static string Convert(string ext)
{
var extMineTypePairs = new[]
{
//(ext: ".jpg", mimeType: "image/jpeg"),
// alt 鼠标下拽
(ext: ".jpg" , mimeType: "image/jpeg" ),
(ext: ".jpeg" , mimeType: "image/jpeg" ),
(ext: ".png" , mimeType: "image/png" ),
(ext: ".gif" , mimeType: "image/gif" ) ,
};
var tuple = extMineTypePairs.FirstOrDefault(i => string.Equals(i.ext, ext, StringComparison.OrdinalIgnoreCase));
if (string.IsNullOrWhiteSpace(tuple.ext)) return string.Empty;
return tuple.mimeType;
}
public static string GetFileMimeType(string filePath)
{
var ext = Path.GetExtension(filePath);
return Convert(ext);
}
}
public static class DataURLConverter
{
public static string ConvertFromFile(string filePath)
{
var mimeType = ExtensionToMIMEConverter.GetFileMimeType(filePath);
var bytes = File.ReadAllBytes(filePath);
var base64Content = Convert.ToBase64String(bytes);
return CreateDataURLBase64Format(mimeType, base64Content);
}
private static string CreateDataURLBase64Format(string mineType, string base64Content)
{
if (string.IsNullOrWhiteSpace(mineType)) return base64Content;
return $"data:{mineType};base64,{base64Content}";
}
}
internal class Program
{
static void Main(string[] args)
{
var dte = Marshal.GetActiveObject("VisualStudio.DTE") as DTE2;
var toolWindows = dte.ToolWindows;
var solutionExplorer = toolWindows.SolutionExplorer;
var result = (solutionExplorer.SelectedItems as IEnumerable).OfType<UIHierarchyItem>();
var selectedItem = result.FirstOrDefault();
var projectItem = selectedItem.Object as ProjectItem;
var selectedPath = projectItem.FileNames[0];
var filePath = selectedPath;
var base64DataUrlContent = DataURLConverter.ConvertFromFile(filePath);
//Console.WriteLine(base64DataUrlContent);
var base64TxtFilePath = GetBase64TxtFilePath(filePath);
File.WriteAllText(base64TxtFilePath, base64DataUrlContent);
System.Diagnostics.Process.Start(base64TxtFilePath);
}
private static string GetBase64TxtFilePath(string sourceFilePath)
{
var dir = Path.GetDirectoryName(sourceFilePath);
var name = Path.GetFileNameWithoutExtension(sourceFilePath);
return Path.Combine(dir, name + "base64.txt");
}
}
}
3. Roslyn 增量生成器
资源需要改成这种形式才能参与解析:
C# 分析器其它文件

using Microsoft.CodeAnalysis;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace ResourceBase64CodeGen
{
public static class ExtensionToMIMEConverter
{
// .jpg => image/jpeg
// .jpeg => image/jpeg
public static string Convert(string ext)
{
var extMineTypePairs = new[]
{
//(ext: ".jpg", mimeType: "image/jpeg"),
// alt 鼠标下拽
(ext: ".jpg" , mimeType: "image/jpeg" ),
(ext: ".jpeg" , mimeType: "image/jpeg" ),
(ext: ".png" , mimeType: "image/png" ),
(ext: ".gif" , mimeType: "image/gif" ) ,
};
var tuple = extMineTypePairs.FirstOrDefault(i => string.Equals(i.ext, ext, StringComparison.OrdinalIgnoreCase));
if (string.IsNullOrWhiteSpace(tuple.ext)) return string.Empty;
return tuple.mimeType;
}
public static string GetFileMimeType(string filePath)
{
var ext = Path.GetExtension(filePath);
return Convert(ext);
}
}
public static class DataURLConverter
{
public static string ConvertFromFile(string filePath)
{
var mimeType = ExtensionToMIMEConverter.GetFileMimeType(filePath);
var bytes = File.ReadAllBytes(filePath);
var base64Content = Convert.ToBase64String(bytes);
return CreateDataURLBase64Format(mimeType, base64Content);
}
private static string CreateDataURLBase64Format(string mineType, string base64Content)
{
if (string.IsNullOrWhiteSpace(mineType)) return base64Content;
return $"data:{mineType};base64,{base64Content}";
}
}
[Generator]
public class ResourceBase64Generator : IIncrementalGenerator
{
public void Initialize(IncrementalGeneratorInitializationContext context)
{
var additionalFiles = context.AdditionalTextsProvider;
context.RegisterSourceOutput(additionalFiles, (sourceProductionContext, file) =>
{
var csproj = GetCsProject(file.Path);
CreateCode(sourceProductionContext, csproj, file.Path);
});
//context.AdditionalTextsProvider
}
public static void CreateCode(SourceProductionContext sourceProductionContext, string csproj, string resouceOrignPath)
{
GetNamespaceAndResourceName(csproj, resouceOrignPath, out var namespaceName, out var constName);
var base64DataURL = DataURLConverter.ConvertFromFile(resouceOrignPath);
var code = $@"using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace {namespaceName}
{{
public static partial class ResourceBase64
{{
public const string {constName} = ""{base64DataURL}"";
}}
}}
";
//Console.WriteLine(code);
var autoCsName = $"{constName}.{namespaceName.Replace(".","_")}.cs";
sourceProductionContext.AddSource(autoCsName, code);
}
private static void GetNamespaceAndResourceName(string csproj, string resourceOriginPath, out string namespaceName, out string constName)
{
// D:\Code\AvaloniaTest\Base64CodeGen\MainProject\MainProject.csproj
// D:\Code\AvaloniaTest\Base64CodeGen\MainProject\Resources\miku.png
// MainProject.Resources
// miku
var rootDir = Path.GetDirectoryName(csproj);
// D:\Code\AvaloniaTest\Base64CodeGen\MainProject
var resourcesDir = Path.GetDirectoryName(resourceOriginPath);
// D:\Code\AvaloniaTest\Base64CodeGen\MainProject\Resources
var subNamespaceToken = resourcesDir.Substring(rootDir.Length);
var rootNamespaceToken = Path.GetFileNameWithoutExtension(csproj);
var subNamespaceTokens = subNamespaceToken
.Split('/', '\\')
.Where(i=>string.IsNullOrWhiteSpace(i) == false);
var list = new List<string>();
list.Add(rootNamespaceToken);
list.AddRange(subNamespaceTokens);
namespaceName = string.Join(".", list);
constName = Path.GetFileNameWithoutExtension(resourceOriginPath);
}
public static string GetCsProject(string originFile)
{
var parentFolder = Path.GetDirectoryName(originFile);
var csproj = GetCsProjectWithFolder(parentFolder, 0);
return csproj;
}
private static string GetCsProjectWithFolder(string folder, int depth)
{
if (depth >= 4) return string.Empty;
if (HasCsProject(folder) ) return GetCsProjectInFolder(folder);
var parentFolder = Path.GetDirectoryName(folder);
return GetCsProjectWithFolder(parentFolder, depth + 1);
}
private static bool HasCsProject(string folder)
{
return !string.IsNullOrWhiteSpace(Directory.GetFiles(folder).FirstOrDefault(i => i.EndsWith(".csproj")));
}
private static string GetCsProjectInFolder(string folder)
{
return Directory.GetFiles(folder).FirstOrDefault(i => i.EndsWith(".csproj"));
}
}
}

浙公网安备 33010602011771号