如何读取可移植类库中的资源文件?

我有一个可移植的库,我用于Windows Phone应用程序。 在同一个可移植库中,我有几个内容文件( Build Action = Content )。

我在Portable Library中创建了一个类DataReader ,它应该向我返回一个流到内容文件。 但是,使用下面的代码,我一直从GetManifestResourceStream返回null 。 我究竟做错了什么?

 public class DataReader { public static Stream GetStream(string code) { string path = string.Format("./data/code-{0}.dat", code); return Assembly.GetExecutingAssembly().GetManifestResourceStream(path); } } 

你的道路是错的。 您正在使用斜杠,但在嵌入式清单资源名称中,斜杠在构建期间转换为句点。 此外,根据您的PCL目标平台,您甚至可能无法调用Assembly.GetExecutingAssembly()

这是你可以做的:

 var assembly = typeof(AnyTypeInYourAssembly).GetTypeInfo().Assembly; // Use this help aid to figure out what the actual manifest resource name is. string[] resources = assembly.GetManifestResourceNames(); // Once you figure out the name, pass it in as the argument here. Stream stream = assembly.GetManifestResourceStream("Some.Path.AndFileName.Ext"); 

来自http://social.msdn.microsoft.com/Forums/windowsapps/en-US/386eb3b2-e98e-4bbc-985f-fc143db6ee36/read-local-file-in-portable-library#386eb3b2-e98e-4bbc-985f -fc143db6ee36

无法在Windowsapp store应用和Windows Phone 8应用之间轻松进行文件访问。 您必须使用特定于平台的代码,才能打开文件并获取流。 然后,您可以将流传递到PCL。

如果使用Content构建操作构建它,则XML不在DLL内部。 它在文件系统上,并且没有办法从PCL内部获取它。 这就是为什么所有答案都将构建操作设置为嵌入式资源的原因 。 它将文件放在MyPCL.DLL\Path\To\Content.xml

但是 ,如果将构建操作设置为“ 内容”并将复制类型设置为“ 复制”(如果较新) ,则会将文件放在与可执行文件相同的目录中。

解决方案Explorer,属性和Windows资源管理器

因此,我们可以在我们的PCL中放置一个用于读取文件的界面。 在启动我们的非便携代码时,我们将一个实现注入PCL。

 namespace TestPCLContent { public interface IContentProvider { string LoadContent(string relativePath); } } namespace TestPCLContent { public class TestPCLContent { private IContentProvider _ContentProvider; public IContentProvider ContentProvider { get { return _ContentProvider; } set { _ContentProvider = value; } } public string GetContent() { return _ContentProvider.LoadContent(@"Content\buildcontent.xml"); } } } 

既然上面已经定义了PCL,我们可以在不可移植的代码中创建我们的接口实现(下面):

 namespace WPFBuildContentTest { class ContentProviderImplementation : IContentProvider { private static Assembly _CurrentAssembly; private Assembly CurrentAssembly { get { if (_CurrentAssembly == null) { _CurrentAssembly = System.Reflection.Assembly.GetExecutingAssembly(); } return _CurrentAssembly; } } public string LoadContent(string relativePath) { string localXMLUrl = Path.Combine(Path.GetDirectoryName(CurrentAssembly.GetName().CodeBase), relativePath); return File.ReadAllText(new Uri(localXMLUrl).LocalPath); } } } 

在应用程序启动时,我们注入实现,并演示加载内容。

 namespace WPFBuildContentTest { //App entrance point. In this case, a WPF Window public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } private void Window_Loaded(object sender, RoutedEventArgs e) { ContentProviderImplementation cpi = new ContentProviderImplementation(); TestPCLContent.TestPCLContent tpc = new TestPCLContent.TestPCLContent(); tpc.ContentProvider = cpi; //injection string content = tpc.GetContent(); //loading } } } 

编辑:为了简单起见,我保留了字符串而不是Streams。

只是回应赏金请求。 首先,使用Build Action = Content实际上并不会影响构建。 它是其他工具可以读取的项目项属性。 例如,安装程序构建器使用它来确定文件是否需要包含在安装程序中并部署到用户的计算机。

正如投票问题中所述,使用Build Action = Embedded Resource是OP的疏忽。 这实际上指示MSBuild将文件作为资源嵌入到程序集清单中,使用Assembly.GetManifestResourceStream()在运行时检索它。

但从赏金评论中可以清楚地看出,你也不想这样做。 后备是将文件复制到目标计算机上。 它会耐心地坐在你需要它之​​前。 值得注意的是,这不会以任何方式改变用户从Store下载的包的大小。 无论是在程序集内部还是程序包中的单独文件中,它都需要相同的空间。

因此,请将其作为一种方法来获得成功。

它确实在运行时有所不同,整个程序集在加载时会映射到虚拟内存中。 因此,具有资源的程序集将占用更多虚拟内存空间。 但“虚拟”这个词非常重要,它只需要很少的手机资源。 页面映射表中只有几个字节,资源中每4096个字节。 在访问虚拟内存之前,您不会开始为虚拟内存付费。 此时手机操作系统需要实际将其从虚拟内存转换为物理内存。 或者换句话说,将资源的字节加载到RAM中。 这与加载文件没有什么不同,它在打开时也会加载到RAM中。

因此,请将其作为一种方法来获得成功。

我们实际上已经没有充分的理由这样做了,微软肯定选择了默认的方式来处理资源作为最佳实践。 它是。 但有时您必须将内容部署为文件,因为它太大了。 一个推动2千兆字节或更多,消耗32位操作系统上的所有虚拟内存,因此无法映射到VM。 该程序根本无法启动。 这不是电话用户非常满意的程序,真的。

然后,您需要关注解决方案的打包构建阶段,这是构建手机应用程序时的最后一步。 编译解决方案中的所有项目以及上载到商店并由用户下载的唯一文件的创建。

是的,那里有一个问题,MSBuild不够智能,无法看到使用该资源的PCL库。 构建操作=内容应该足够好,就像安装程序一样,但这不起作用。 它只会打包DLL,而不是资源。 它是假设你嵌入它,最佳实践解决方案。

您需要做的是覆盖包清单。 在这篇MSDN文章中描述。 非常非常难看,你正看着一个空白的闪烁光标。 这是我的建议,这是不做的。

将文件添加到可移植资源,并将构建操作设置为Embedded Resource 。 例如,文件夹CountryFlags下的文件GB.pngUS.png

使用这样的代码添加一个getter函数(这里是我们countryflag getter图像的特定代码)。

 public class CountryFlags { public static Stream GetFlagStream(string countryIsoCode2ch) { var flagname = "Full.DLL.Name.CountryFlags.{0}.png"; var rs = Assembly.GetExecutingAssembly().GetManifestResourceStream( string.Format(flagname, countryIsoCode2ch)); return rs; } } 

这里Full.DLL.Name是生成的可移植库的一部分,它在.dll扩展名之前。 ( 注意: Anything.Resources.dll是一个库的错误名称,因为它至少在生成XAP等时被Visual Studio忽略;相反,例如Anything.PortableResource.dll将起作用)。

如果您已将文件添加为资源,请检查.Designer.cs,每个资源都有一个属性。 你可以从那里访问。

这是dat文件资源的示例自动生成属性

  internal static byte[] MyDatFile { get { object obj = ResourceManager.GetObject("MyDatFile", resourceCulture); return ((byte[])(obj)); } 

你可以读取dat文件

  System.Text.UTF8Encoding enc = new System.Text.UTF8Encoding(); var str = enc.GetString(Resource1.MyDatFile); 
 var assembly = typeof(PegHelper).GetTypeInfo().Assembly; using (var stream = assembly.GetManifestResourceStream("Parsers.Peg.SelfDef.xml")) using (var reader = new StreamReader(stream)) { string xmlText = reader.ReadToEnd(); return XDocument.Parse(xmlText); } 

首先,像这样检索你的程序集(DataLoader是你的PCL程序集中的一个类):

 var assembly = typeof(DataLoader).GetTypeInfo().Assembly; 

将文件添加到可移植资源, 并将构建操作设置为Embedded Resource

然后你可以像这样检索你的资源:

 string resourceNam= "to be filled"; var assembly = typeof(DataLoader).GetTypeInfo().Assembly; var compressedStream = assembly.GetManifestResourceStream(resourceName)); 

例如,如果我在程序集“TvShowTracker.Helpers”中的文件夹“Assets / Logos”中有一个文件logo.png,我将使用以下代码:

 string resourceNam= "TvShowTracker.Helpers.Assets.Logos.logo.png"; var assembly = typeof(DataLoader).GetTypeInfo().Assembly; var compressedStream = assembly.GetManifestResourceStream(resourceName)); 

快乐编码:)

您需要使用Application.GetResourceStream方法而不是使用GetManifestResource流

参考: http : //msdn.microsoft.com/en-us/library/ms596994%28v=vs.95%29.aspx

 var albumArtPlaceholder = Application.GetResourceStream( new Uri("Images/artwork.placeholder.png", UriKind.Relative));