首页 文章

Xamarin.Forms:如何将Resources中的图像加载到字节数组中?

提问于
浏览
5

我有一个(希望)简单的问题(我没有找到答案,这符合我所有的搜索) .

我使用Xamarin.Forms 1.4.1-Pre-1 . 在我的应用程序中,我有:

byte[] AvatarErfassung; // Bytearray, to later update the webservice
var Avatar = new Image { HeightRequest = 71, WidthRequest = 61, HorizontalOptions = LayoutOptions.Start };
Avatar.Source = "SymbolMann.jpg";

图像“SymbolMann.jpg” is included as project-resource (在每个项目中)并显示在页面上(没有问题) .
我现在想将图像放在一个字节数组中,以将其发送到我们的web服务 . 我没有找到任何方法来访问图像"SymbolMann.jpg"(以字节数组加载它)或使用(但是)Avatar.Source .

Question:
How to get the image “SymbolMann.jpg” into the byte-array “AvatarErfassung” ?

Thanks for every answer

嗨迪米特里斯

感谢您的(快速)重播 .
正如我写的那样,我和Xamarin一起工作 . Forms .
图像存储为 resources :在\ Resources \ Drawable \(对于Android),\ Resources \(对于iOS)和根(对于WP) . 我必须在内容页面上加载图像 .

如果我超越你的代码,就行:

var assembly = this.GetType().GetTypeInfo().Assembly;

我有错误信息(翻译成英文):
“System.Type不包含GetTypeInfo方法的定义(使用丢失?)”

Do I have to add a specific using?

您编写://您可以将“this.GetType()”替换为“typeof(MyType)”,其中MyType是程序集中的任何类型 .

What do I have to place as type?

= typeof(????).GetTypeInfo().Assembly:

Thanks for a further reply.

更新#2:

第一,谢谢你的耐心......

不幸的是在 VS2013 中,我没有找到任何显示我缺少使用功能的“解决方案” - 但是我已经找到了使用我自己的缺失:-)
与此同时,我添加了使用System.Reflection;

现在,行:var assembly = this.GetType() . GetTypeInfo() . Assembly;

解决和编译没有错误

此外,我已将.jpg的类型从“Android资源”更改为“嵌入式资源” .
该应用程序然后在行中 crashes :long length = s.Length; as it seems that s is null (I think the jpg is not found)

进一步 - 如果我将类型更改为“嵌入式资源” - the image is not found any more by Xamarin.Forms (Avatar.Source = "SymbolMann.jpg";)

如果我将.jpg的类型更改为“编译”,我无法编译应用程序 .
错误消息:命名空间不能直接包含字段或方法等成员 .

So… what is the correct type to the resource (so that it can be loaded with assembly.GetManifestResourceStream()?

Do I have to add something to the filename (SymbolMann.jpg) so that it will be found?

How do I have to change Avatar.Source = "SymbolMann.jpg" so that it is found (if I change the type from “Android Resource” to anything else)?

我的需求再一次:
在注册页面上,默认图像(符号)在标准Xamarin.Forms图像(avatar.source =“SymbolMann.jpg”/“SymbolFrau.jpg”)中显示为男人和女人的化身 .
.jpg存储在每个项目(Android,iOS和WP)的标准目录中,可以在没有图像对象问题的情况下访问它们 .
然后,用户可以使用其中一个默认图像或通过mediapicker加载另一个默认图像 .
如果用户点击“注册”按钮,我必须从Image创建一个字节数组,通过json将其发送到我们的webservice .
如果用户通过mediapicker选择另一个图像,那么问题是,从默认图像(在每个平台中)成为一个字节数组 .

再次感谢您的帮助......

6 回答

  • 2

    添加到noelicus's answer

    设置

    • 将您的图像添加到Xamarin.Forms项目的根文件夹,例如 MyProject\pizza.png

    • 在Visual Studio中,右键单击图像文件并选择 Build Action - > EmbeddedResource

    enter image description here

    代码

    public byte[] GetImageFromFile(string fileName)
    {
        var applicationTypeInfo = Application.Current.GetType().GetTypeInfo();
    
        byte[] buffer;
        using (var stream = applicationTypeInfo.Assembly.GetManifestResourceStream($"{applicationTypeInfo.Namespace}.fileName"))
        {
            if (stream != null)
            {
                long length = stream.Length;
                buffer = new byte[length];
                stream.Read(buffer, 0, (int)length);
            }
        }
    
        return buffer;
    }
    

    示例

    public class MyPage() : ContentPage
    {
        public MyPage()
        {
            var imageAsByteArray = GetImageFromFile("pizza.png");
    
            var pizzaImage = new Image();
            pizzaImage.Source = ImageSource.FromStream(() => new MemoryStream(imageAsByteArray));
    
            Content = pizzaImage;
        }
    
        byte[] GetImageFromFile(string fileName)
        {
             var applicationTypeInfo = Application.Current.GetType().GetTypeInfo();
    
            byte[] buffer;
            using (var stream = applicationTypeInfo.Assembly.GetManifestResourceStream($"{applicationTypeInfo.Namespace}.fileName"))
            {
                if (stream != null)
                {
                    long length = stream.Length;
                    buffer = new byte[length];
                    stream.Read(buffer, 0, (int)length);
                }
            }
    
            return buffer;
        }
    }
    
  • 1

    您可以通过从程序集中读取资源流来轻松完成此操作:

    var assembly = this.GetType().GetTypeInfo().Assembly; // you can replace "this.GetType()" with "typeof(MyType)", where MyType is any type in your assembly.
    byte[] buffer;
    using (Stream s = assembly.GetManifestResourceStream("SymbolMann.jpg”))
    {
        long length = s.Length;
        buffer = new byte[length];
        s.Read(buffer, 0, (int)length);
    }
    // Do something with buffer
    

    编辑:

    要获取包含项目的嵌入资源的程序集,需要在该程序集中包含的类型上调用 GetTypeInfo 方法 . 因此, typeof(X) ,其中"X"是程序集中的任何类型 . 例如,如果您使用名为 MyCustomPage 的自定义页面:

    public class MyCustomPage : ContentPage {}
    

    ......这就是你要通过的: typeof(MyCustomPage)

    您需要 Type 类型的任何实例(这是 typeof 关键字基本上返回的内容) . 另一种方法是在项目中包含的任何对象上调用 GetType() 方法 .

    请注意,如果对项目中未包含的类型调用 typeof(X) ,则无法获得正确的程序集 . 例如 . 调用 typeof(ContentPage).GetTypeInfo().Assembly ,将返回 ContentPage 类型所在的程序集 . 哪个不包含您的资源 . 我希望这不会令人困惑 . 如果是,请告诉我 .

    现在, GetTypeInfo 方法是 System.Reflection 名称空间中包含的扩展方法 . 当Xamarin Studio中的文本编辑器无法识别某些内容时,它会用红色突出显示它 . 右键单击它将在上下文菜单中为您提供Resolve选项:

    Right-click in Xamarin Studio

    如果可以解析类型或方法,它将显示该选项 .

    更多关于Xamarin Forms的图片here .

  • 7

    First, many thanks to Dimitris who has guided me to the right direction! 

    Conclusion:

    The base:
    我使用VS2013 - 更新2,Xamarin.Forms(1.4.1-Pre1),我的应用程序基于模板 “Blank App (Xamarin.Forms Shared)” (不是PCL),我使用所有平台(Android,iOS,WP) .

    在用户注册页面上,用户可以选择图像作为化身(稍后显示给他的帖子) . 有两个默认图像(一个用于根据使用者的性别,显示出男性的女性) . 然后,用户可以超过其中一个默认图像或从媒体选择器中选择另一个图像 .
    默认图像存储在内容图像的默认位置(对于Android,例如 /Resources/Drawable/ 下) .
    图像显示在XF-Standard-Image-Object中,Image-Source =“xxx.jpg” .
    这没有问题 .

    The problem:
    由于用户数据(包括头像)存储在SQL-Server上(通过Json-web-service),我必须将图像数据转换为字节数组(然后将其与其他数据一起发送为字符串到网络服务) .
    问题是, to load the images from the default-directories 并将其转换为字节数组(其中 the file-access was the problem - >无法访问) .

    The solution:
    感谢Dimitris指导我正确的方向,我实施了以下解决方案:

    在所有项目(Android,iOS和WP)中,我添加了 new directory “\Embedded\” (directly under the root) . 然后,我在这个目录中复制了我的两个jpg(“SymbolMann.jpg”和“SymbolFrau.jpg”)(适用于所有平台) .
    然后,我已将资源类型更改为来自例如的图像 . “AndroidResource”改为 “Embedded Resource” (适用于所有平台的相同类型) .

    在我的应用程序启动代码(app.cs)中,我添加了:

    //
    string cNameSpace = "";
    switch (Device.OS)
    {
      case TargetPlatform.WinPhone:
        cNameSpace = "MatrixGuide.WinPhone";
        break;
      case TargetPlatform.iOS:
        cNameSpace = "MatrixGuide.iOS";
        break;
      case TargetPlatform.Android:
        cNameSpace = "MatrixGuide.Droid";
        break;
    }
    GV.cEmbeddedAblage = cNameSpace + ".Embedded.";
    

    其中GV.cEmbeddedAblage只是一个全局变量 .
    要检查命名空间名称,我必须右键单击每个平台的项目,并查看已确定的命名空间名称(可能在您的项目中有所不同) .
    .Embedded . 是新创建的目录(在所有平台上都是相同的名称) .

    另外,我为男性图像(GV.ByteSymbolMann)和女性图像(GV.ByteSymbolFrau)创建了全局字节数组 .

    在注册页面的启动代码中,我添加了:

    string cFilename = "";
    if (GV.ByteSymbolMann == null) // not yet loaded
    {
      cFilename = GV.cEmbeddedAblage + "SymbolMann.jpg";
      var assembly = this.GetType().GetTypeInfo().Assembly; 
      byte[] buffer;
      using (Stream s = assembly.GetManifestResourceStream(cFilename))
       {
         long length = s.Length;
         buffer = new byte[length];
         s.Read(buffer, 0, (int)length);
         GV.ByteSymbolMann = buffer; // Overtake byte-array in global variable for male
       }
    }
    //
    if (GV.ByteSymbolFrau == null) // not yet loaded
    {
      cFilename = GV.cEmbeddedAblage + "SymbolFrau.jpg";
      var assembly = this.GetType().GetTypeInfo().Assembly; 
      byte[] buffer;
      using (Stream s = assembly.GetManifestResourceStream(cFilename))
       {
         long length = s.Length;
         buffer = new byte[length];
         s.Read(buffer, 0, (int)length);
         GV.ByteSymbolFrau = buffer; // Overtake byte-array in global variable for female
        }
    }
    

    将.jpg加载到图像对象的代码,我不得不改为:

    //Avatar.Source = "SymbolMann.jpg";  // Load from default directory
    

    Avatar.Source = ImageSource.FromStream(() => new MemoryStream(GV.ByteSymbolMann)); // Load (stream) from byte-array
    

    然后我在另一个字节数组中接管选定的字节数组(并行显示图像)(以便稍后以字符串形式上传到Web服务)

    AvatarErfassung = GV.ByteSymbolMann;
    

    如果用户更改选择(例如,更改为女性):

    Avatar.Source = ImageSource.FromStream(() => new MemoryStream(GV.ByteSymbolFrau)); 
    AvatarErfassung = GV.ByteSymbolFrau;
    

    注意:如果用户从mediapicker中选择另一个图像,我可以直接访问该流,因此这绝不是问题所在 .

    This solution works for all platforms (Android, iOS, WP).

    I hope this may help some other users… 

    And... finally thanks to Dimitris

  • 11

    谢谢@FredWenger和@Dimitris - 这是我的解决方案,基于你的答案:

    • 将您的图像添加到Xamarin Forms(便携式)项目中,无论您喜欢什么 . 我只是去了根: MyProject\DaffyDuck.jpg

    • 将图像标记为 Embedded Resource (文件属性 - >构建操作)

    • 调用引用嵌入式资源的函数,如下所示: ImageDataFromResource("MyProject.DaffyDuck.jpg")
      请注意,您需要包含项目名称以获取程序集名称* .

    • 这是功能:

    public byte[] ImageDataFromResource(string r)
    {
        // Ensure "this" is an object that is part of your implementation within your Xamarin forms project
        var assembly = this.GetType().GetTypeInfo().Assembly; 
        byte[] buffer = null;
    
        using (System.IO.Stream s = assembly.GetManifestResourceStream(r))
        {
            if (s != null)
            {
                long length = s.Length;
                buffer = new byte[length];
                s.Read(buffer, 0, (int)length);
            }
        }
    
        return buffer;
    }
    
    • 如果需要在ImageView控件中引用加载的数据,请执行以下操作:
      ImageView.Source = ImageSource.FromStream(() => new MemoryStream(imageData));

    *注意:如果步骤3不起作用并且您想要检查嵌入的资源名称,请调用: var names = assembly.GetManifestResourceNames(); 并查看项目中列出的内容 .

  • 2

    让这个工作起来我遇到了很多问题 . 我的项目已在其他地方使用FFImageLoading,我发现这是将.png作为流加载的最简单方法:

    using (var stream = await ImageService.Instance.LoadCompiledResource("resource_image.png").AsPNGStreamAsync())
    {
        localBitmap = SKBitmap.Decode(stream);
    }
    

    确保您的资源文件在iOS和Droid项目中具有相同的名称 . 他们可能已经将他们的构建操作设置为'BundleResource'(iOS)或'AndroidResource'(Droid) .

    当然,你必须安装FFImageLoading Nuget,但你会很高兴你做到了 .

  • 7

    我使用noelicus给出了想法并打印了ManifestResourceNames数组中的所有名称,我发现我的图像名称为 ProjectName.Droid.image_name.jpeg . 然后我将方法调用从 assembly.GetManifestResourceStream("image_name.jpg") 更改为 assembly.GetManifestResourceStream("ProjectName.Droid.image_name.jpeg") . 它有效 . 所以正确的答案是:

    • 将图像存储在PCL的根文件夹中 .

    • 将其作为嵌入式资源的属性中的构建操作 .

    • 然后打印 <b><i>var names = assembly.GetManifestResourceNames()</i></b> 数组,该数组将是一个字符串数组,可以使用打印

    foreach(var data in names){Debug.WriteLine(data);}
    
    • 在那里,您将看到图像的资源名称,然后使用该方法
    Stream s = assembly.GetManifestResourceStream("resource_name_you_found.format")
    

相关问题