.NET 提供兩種資源檔案格式:.resx(XML 格式,設計階段編輯)與 .resources(二進制格式,執行階段載入)。本文詳細說明如何嵌入二進制檔案(圖片、文檔、DLL 等)並透過 ResourceManager 讀取。
| 特性 | .resx | .resources |
|---|---|---|
| 格式 | XML 文字 | 二進制 |
| 設計工具支援 | Visual Studio 內建編輯器 | 需用工具生成 |
| 支援資料型別 | 字串、影像、圖示、檔案參考 | 字串、位元組陣列、序列化物件 |
| 編譯輸出 | 編譯為 .resources 嵌入組件 | 直接嵌入組件 |
| 適用場景 | 本地化字串、UI 資源 | 大型二進制檔案、外掛載入 |
專案右鍵 → Add → New Item → Resources File → 命名為 Resources.resx
template.pdf、logo.png)
using System.Resources;
using System.Reflection;
public class ResourceService
{
private static readonly ResourceManager _rm = new ResourceManager(
"MyProject.Resources",
Assembly.GetExecutingAssembly());
// 讀取位元組陣列(檔案內容)
public static byte[] GetFileBytes(string resourceName)
{
return _rm.GetObject(resourceName) as byte[];
}
// 讀取字串(文字檔)
public static string GetTextFile(string resourceName)
{
byte[] bytes = GetFileBytes(resourceName);
return bytes != null ? Encoding.UTF8.GetString(bytes) : null;
}
// 另存為檔案
public static void ExtractToFile(string resourceName, string outputPath)
{
byte[] data = GetFileBytes(resourceName);
if (data != null)
File.WriteAllBytes(outputPath, data);
}
}
// 使用範例
byte[] pdfBytes = ResourceService.GetFileBytes("template");
ResourceService.ExtractToFile("logo", @"C:\Temp\logo.png");
using System.Resources;
public static void CreateResourcesFile()
{
string outputPath = @"..\..\MyResources.resources";
using (ResourceWriter writer = new ResourceWriter(outputPath))
{
// 加入字串
writer.AddResource("AppTitle", "我的應用程式");
// 加入位元組陣列(檔案內容)
byte[] pdfData = File.ReadAllBytes(@"C:\Templates\contract.pdf");
writer.AddResource("ContractTemplate", pdfData);
// 加入影像
using (var img = Image.FromFile(@"C:\Images\logo.png"))
using (var ms = new MemoryStream())
{
img.Save(ms, ImageFormat.Png);
writer.AddResource("LogoImage", ms.ToArray());
}
writer.Generate();
}
}
在專案檔中設定:
<ItemGroup>
<EmbeddedResource Include="MyResources.resources" />
</ItemGroup>
或在 Visual Studio:檔案屬性 → Build Action = Embedded Resource
public static class EmbeddedResourceReader
{
// 命名空間.檔案名(不含副檔名)
private const string ResourceName = "MyProject.MyResources";
public static byte[] GetResource(string name)
{
using (Stream stream = Assembly.GetExecutingAssembly()
.GetManifestResourceStream(ResourceName))
{
if (stream == null) return null;
using (ResourceReader reader = new ResourceReader(stream))
{
foreach (DictionaryEntry entry in reader)
{
if (entry.Key.ToString() == name)
return entry.Value as byte[];
}
}
return null;
}
}
}
// 建立語系專用資源檔
// Resources.zh-TW.resx, Resources.en-US.resx, Resources.ja-JP.resx
// 程式碼自動根據 CurrentUICulture 選擇
Thread.CurrentThread.CurrentUICulture = new CultureInfo("zh-TW");
string title = Resources.AppTitle; // 強型別存取(需啟用自訂工具)
public static void ExtractLargeResource(string resourceName, string outputPath)
{
using (Stream resourceStream = Assembly.GetExecutingAssembly()
.GetManifestResourceStream($"MyProject.Resources.{resourceName}"))
using (FileStream fileStream = new FileStream(outputPath, FileMode.Create))
{
resourceStream?.CopyTo(fileStream); // 串流複製,不載入完整記憶體
}
}
public static ResourceManager LoadExternalResources(string dllPath)
{
Assembly assembly = Assembly.LoadFrom(dllPath);
// 假設資源命名為 PluginName.Resources
string resourceName = assembly.GetName().Name + ".Resources";
return new ResourceManager(resourceName, assembly);
}
| 問題 | 原因 | 解決 |
|---|---|---|
| GetObject 回傳 null | 資源名稱不符(區分大小寫、命名空間前綴) | 用 GetManifestResourceNames() 列出所有資源名稱確認 |
| 編譯後找不到資源 | Build Action 非 Embedded Resource | 檔案屬性設為 Embedded Resource |
| 中文檔名亂碼 | 資源名稱編碼問題 | 資源名稱僅用 ASCII,實際檔名存放在資源內容中 |
| 記憶體不足 | 大檔案一次性載入 byte[] | 使用 GetManifestResourceStream 串流處理 |
IFileProvider 統一存取嵌入檔案與實體檔案Microsoft.Extensions.FileProviders.Embedded 管理掌握 .resx/.resources 嵌入與讀取技巧,可將設定檔、範本、靜態資產封裝在組件內,簡化部署。現代專案優先使用強型別 .resx 配合 Source Generator,大型二進制檔案採串流讀取,外掛架構則動態載入獨立 .resources 組件。