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

时间:2012-06-09 19:06:01

标签: c# .net embedded-resource

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

我在Portable Library中创建了一个类DataReader,它应该向我返回一个流到内容文件。但是,根据以下代码,我始终从null返回GetManifestResourceStream。我做错了什么?

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

8 个答案:

答案 0 :(得分:34)

你的道路是错的。您正在使用斜杠,但在嵌入式清单资源名称中,斜杠在构建期间转换为句点。 此外,根据您的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");

答案 1 :(得分:9)

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

  

无法在Windows应用商店应用和Windows之间进行文件访问   电话8个应用。您必须使用特定于平台的代码才能打开该文件   并获得一个流。然后,您可以将流传递到PCL。

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

,如果您将构建操作设置为内容并将复制类型设置为复制更新,则会将您的文件放入与可执行文件相同的目录。

Solution Explorer, Properties, and Windows Explorer

因此,我们可以在我们的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。

答案 2 :(得分:3)

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

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

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

因此,请将其作为开始前进的方法。

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

因此,请将其作为开始前进的方法。

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

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

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

您需要做的是覆盖包清单。在this MSDN article中描述。非常,非常丑陋,你正在看一个空白的闪烁光标。这就是我提出好建议的地方,这是不做的。

答案 3 :(得分:2)

将您的文件添加到便携式资源,并将构建操作设置为嵌入式资源。例如文件夹GB.png下的文件US.pngCountryFlags

使用这样的代码添加一个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将起作用。 / p>

答案 4 :(得分:0)

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

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

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

您可以将文件读取为

    System.Text.UTF8Encoding enc = new System.Text.UTF8Encoding();
    var str = enc.GetString(Resource1.MyDatFile);

答案 5 :(得分:0)

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

答案 6 :(得分:0)

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

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

将您的文件添加到便携式资源,将构建操作设置为嵌入式资源

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

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

快乐编码:)

答案 7 :(得分:-1)

您需要使用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));