22

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

我在便携式库中创建了一个类DataReader,它应该将流返回给内容文件。但是,使用下面的代码,我一直nullGetManifestResourceStream. 我究竟做错了什么?

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

8 回答 8

35

你的路径是错误的。您正在使用斜杠,但在嵌入式清单资源名称中,斜杠在构建期间被转换为句点。此外,根据您的 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");
于 2013-12-07T20:48:07.500 回答
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 Phone 8 应用程序之间进行文件访问。您将不得不使用特定于平台的代码来打开文件并获取流。然后,您可以将流传递到 PCL。

如果您使用内容构建操作构建它,则 XML 不在 DLL 中。它在文件系统上,无法从 PCL 内部获取。这就是为什么所有答案都将构建操作设置为Embedded Resource。它将文件放在MyPCL.DLL\Path\To\Content.xml.

但是,如果您将构建操作设置为Content并将复制类型设置为Copy if newer,它会将您的文件放在与可执行文件相同的目录中。

解决方案资源管理器、属性和 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。

于 2013-11-01T00:31:52.227 回答
3

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

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

但是从赏金评论中可以清楚地看出你也不想要那个。回退是仅将文件复制到目标机器上。它会耐心地坐在哪里,直到你需要它。值得注意的是,这不会任何方式改变用户从商店下载的包的大小。它占用相同数量的空间,无论是在程序集内部还是包中的单独文件。

因此,将其作为一种取得成功的方式。

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

因此,将其作为一种取得成功的方式。

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

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

是的,那里有一个问题,MSBuild 不够聪明,无法看到使用该资源的 PCL 库。Build action = Content 应该足够好,就像安装程序一样,但这不起作用。它只会打包DLL,而不是资源。它假设您已嵌入它,这是最佳实践解决方案。

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

于 2013-10-31T23:23:08.893 回答
2

将您的文件添加到可移植资源并将构建操作设置为Embedded Resource。例如文件夹下的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会起作用)。

于 2012-11-15T08:49:05.157 回答
0

如果您已将文件添加为资源,请检查您的 .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);
于 2012-06-09T19:30:21.457 回答
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);
}
于 2015-01-20T05:51:10.540 回答
0

首先,像这样检索您的程序集(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));

快乐编码:)

于 2017-08-10T06:56:21.623 回答
-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)); 
于 2012-06-09T21:18:59.123 回答