简体   繁体   English

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

[英]How to read a resource file within a Portable Class Library?

I have a Portable Library which I am using for a Windows Phone application.我有一个用于 Windows Phone 应用程序的便携式库。 In that same Portable Library, I have a couple of content files ( Build Action = Content ).在同一个便携式库中,我有几个内容文件( Build Action = Content )。

I created a class DataReader in the Portable Library which is supposed to return me a stream to the content file.我在便携式库中创建了一个类DataReader ,它应该将流返回到内容文件。 However, with the code below I am consistently getting back null from GetManifestResourceStream .但是,使用下面的代码,我始终从GetManifestResourceStream返回null What am I doing wrong?我究竟做错了什么?

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

Your path is wrong.你的路径是错误的。 You're using slashes, but in the embedded manifest resource names slashes were converted to periods during the build.您正在使用斜杠,但在嵌入的清单资源名称中,斜杠在构建过程中被转换为句点。 Also depending on your PCL targeted platforms, you may not even be able to call Assembly.GetExecutingAssembly() .此外,根据您的 PCL 目标平台,您甚至可能无法调用Assembly.GetExecutingAssembly()

Here is what you can do:您可以执行以下操作:

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");

From 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来自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

File access cannot be done portably between Windows Store apps and Windows Phone 8 apps.无法在 Windows 应用商店应用程序和 Windows Phone 8 应用程序之间轻松完成文件访问。 You will have to use platform specific code, to open the file and acquire a stream.您必须使用特定于平台的代码来打开文件并获取流。 You can then pass the stream into the PCL.然后,您可以将流传递到 PCL。

If you build it with the Content build action, the XML is not inside of the DLL.如果使用内容构建操作构建它,则 XML 不在 DLL 内。 It's on the filesystem, and there's no way to get it from inside of the PCL.它位于文件系统上,无法从 PCL 内部获取它。 That is why all of the answers set the build action to Embedded Resource .这就是为什么所有答案都将构建操作设置为Embedded Resource It places the file inside MyPCL.DLL\\Path\\To\\Content.xml .它将文件放在MyPCL.DLL\\Path\\To\\Content.xml

However , if you set the build action to Content and set the copy type to Copy if newer , it will place your files in the same directory as the executable.但是,如果您将构建操作设置为Content并将复制类型设置为Copy if newer ,则会将您的文件放置在与可执行文件相同的目录中。

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

Therefore, we can just place an interface for reading the file in our PCL.因此,我们可以在我们的 PCL 中放置一个读取文件的接口。 On startup of our nonportable code, we inject an implementation into the 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");
        }
    }
}

Now that the PCL is defined above, we can create our interface implementation in nonportable code (below):既然上面已经定义了 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);
        }
    }
}

On application startup, we inject the implementation, and demonstrate loading contents.在应用程序启动时,我们注入实现,并演示加载内容。

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
        }
    }
}

EDIT: I kept it strings instead of Streams for simplicity.编辑:为简单起见,我将它保留为字符串而不是 Streams。

Just responding to the bounty request.只是回应赏金请求。 First off, using Build Action = Content does not actually affect the build at all.首先,使用 Build Action = Content 实际上根本不会影响构建。 It is a project item property that other tooling can read.它是其他工具可以读取的项目项属性。 An installer builder uses it for example to figure out that the file needs to be included in the setup program and deployed to the user's machine.例如,安装程序构建器使用它来确定文件需要包含在安装程序中并部署到用户的机器上。

Using Build Action = Embedded Resource as noted in the upvoted question was the OP's oversight.使用 Build Action = Embedded Resource 如upvoted 问题中所述是 OP 的疏忽。 That actually instructs MSBuild to embed the file as a resource in the assembly manifest, using Assembly.GetManifestResourceStream() retrieves it at runtime.这实际上指示 MSBuild 将文件作为资源嵌入程序集清单中,使用 Assembly.GetManifestResourceStream() 在运行时检索它。

But it is pretty clear from the bounty comment that you don't want that either.但从赏金评论中可以清楚地看出,您也不希望那样。 The fallback is to just copy the file onto the target machine.回退是将文件复制到目标机器上。 Where it will sit patiently until you need it.它会耐心地坐在那里,直到您需要它为止。 Notable about that is this does not in any way alter the size of the package that the user downloads from the Store.值得注意的关于这是以任何方式改变包的大小从存储用户下载。 It takes the same amount of space, whether it it inside the assembly or a separate file in the package.它占用相同数量的空间,无论是在程序集中还是在包中的单独文件中。

So scratch that as a way to get ahead.因此,将其作为获得成功的一种方式。

It does make a difference at runtime, the entire assembly gets mapped into virtual memory when it gets loaded.它在运行时确实有所不同,整个程序集在加载时会映射到虚拟内存中。 So an assembly with a resource will take more virtual memory space.因此,具有资源的程序集将占用更多的虚拟内存空间。 But the word "virtual" is very important, it takes very few resources of the phone.但是“虚拟”这个词很重要,它占用的手机资源很少。 Just a few bytes in the page mapping tables for every 4096 bytes in the resource.对于资源中的每 4096 个字节,页面映射表中只有几个字节。 You don't start paying for virtual memory until it gets accessed.在虚拟内存被访问之前,您不会开始为虚拟内存付费。 At which point the phone operating system needs to actually turn it from virtual into physical memory.此时手机操作系统需要将其从虚拟内存实际转换为物理内存。 Or in other words, load the bytes of the resource into RAM.或者换句话说,将资源的字节加载到 RAM 中。 This is not different from loading a file, it also gets loaded into RAM when you open it.这与加载文件没有什么不同,它也会在您打开文件时加载到 RAM 中。

So scratch that as a way to get ahead.因此,将其作为获得成功的一种方式。

We're running out of good reasons to actually do this, Microsoft certainly did pick the default way to handle resources as a best-practice.我们没有充分的理由来实际执行此操作,Microsoft 确实选择了默认方式来处理资源作为最佳实践。 It is.这是。 But sometimes you have to deploy content as a file, simply because it is too large.但有时您必须将内容部署为文件,仅仅是因为它太大了。 One that's pushing 2 gigabytes or more, consuming all virtual memory on a 32-bit operating system so cannot possibly be mapped to VM.一个正在推动 2 GB 或更多,消耗 32 位操作系统上的所有虚拟内存,因此不可能映射到 VM。 The program simply won't be able to start.该程序根本无法启动。 This is not the kind of program a phone user is going to be very pleased with, really.这不是电话用户会非常满意的那种程序,真的。

You then need to focus on the packing build phase of the solution, the last step when a phone app gets built.然后,您需要专注于解决方案的打包构建阶段,这是构建手机应用程序的最后一步。 The one where all of the projects in the solution have been compiled and the one-and-only file that's uploaded to the Store, and is downloaded by the user, is created.已编译解决方案中的所有项目,并创建上传到应用商店并由用户下载的唯一文件。

And yes, there's a problem there, MSBuild is not smart enough to see the PCL library using the resource.是的,那里有一个问题,MSBuild 不够聪明,无法查看使用该资源的 PCL 库。 The Build action = Content ought to be good enough, like it is for an installer, but that doesn't work. Build action = Content 应该足够好,就像安装程序一样,但这不起作用。 It will only package the DLL, not the resource.只会打包 DLL,不会打包资源。 It was made to assume you'd embed it, the best practice solution.它假定您已嵌入它,这是最佳实践解决方案。

What you have to do is to override the package manifest.您需要做的是覆盖包清单。 Described in this MSDN article .这篇 MSDN 文章中描述。 Very, very ugly, you're looking at a blank blinking cursor.非常非常丑陋,你看到的是一个空白的闪烁光标。 Which is where I'm running out of good advice, this was made to not do.这是我没有好的建议的地方,这是不做的。

Add your file to portable resource and set build action to Embedded Resource .将您的文件添加到可移植资源并将构建操作设置为Embedded Resource For example files GB.png , US.png under folder CountryFlags .例如文件夹CountryFlags下的文件GB.pngUS.png

Add a getter function with code like this (here it's specific for our countryflag getter image).使用这样的代码添加一个 getter 函数(这里是特定于我们的国旗 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;
    }
}

Here Full.DLL.Name is the part of generated portable library that is before .dll extension.这里Full.DLL.Name是生成的可移植库的一部分,位于.dll扩展名之前。 ( Note: Anything.Resources.dll is a bad name for a library because it gets ignored by Visual Studio at least when generating XAP etc.; instead for example Anything.PortableResource.dll will work). 注意: Anything.Resources.dll对于库来说是一个坏名字,因为它至少在生成 XAP 等时会被 Visual Studio 忽略;例如, Anything.PortableResource.dll可以工作)。

if you have added files as resources, check your .Designer.cs, there will be a property for each resource.如果您已添加文件作为资源,请检查您的 .Designer.cs,每个资源都会有一个属性。 you can access from that.你可以从中访问。

here is the sample auto generate property for dat file resources这是 dat 文件资源的示例自动生成属性

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

you can read the dat file as您可以将 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);
}

First of all, retrieve your assembly like this (DataLoader being a class in your PCL assembly) :首先,像这样检索您的程序集(DataLoader 是您的 PCL 程序集中的一个类):

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

Add your file to portable resource and set build action to Embedded Resource .将您的文件添加到可移植资源并将构建操作设置为 Embedded Resource

Then you can retrieve your ressource like this :然后你可以像这样检索你的资源:

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

For example if I have a file logo.png in a folder "Assets/Logos" in an assembly "TvShowTracker.Helpers" I will use this code :例如,如果我在程序集“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));

Happy coding :)快乐编码:)

You need to use Application.GetResourceStream method instead of using GetManifestResource stream您需要使用 Application.GetResourceStream 方法而不是使用 GetManifestResource 流

Reference: http://msdn.microsoft.com/en-us/library/ms596994%28v=vs.95%29.aspx参考: 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)); 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM