【问题标题】:Xamarin.Forms: How to load an image from Resources into a byte-array?Xamarin.Forms:如何将资源中的图像加载到字节数组中?
【发布时间】:2018-04-26 04:50:56
【问题描述】:

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

我使用 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”包含在项目资源中(在每个项目中)并显示在页面上(没有问题)。
我现在想将图像放在一个字节数组中以将其发送到我们的 Web 服务。 我没有找到任何方法来访问图像“SymbolMann.jpg”(将其加载到字节数组中)或使用(但是)Avatar.Source。

问题:
如何将图像“SymbolMann.jpg”放入字节数组“AvatarErfassung”中?

感谢您的每一个回答

你好迪米特里斯

感谢您(快速)重播。
正如我所写,我使用 Xamarin。Forms
图像存储为资源: 在 \Resources\Drawable\(适用于 Android)、\Resources\(适用于 iOS)和根目录中(适用于 WP)。 我必须在内容页面上加载图像。

如果我超过了你的代码,到那一行:

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

我收到错误消息(翻译成英文):
“System.Type 不包含 GetTypeInfo 方法的定义(缺少 using 吗?)”

我必须添加特定的使用吗?

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

我必须输入什么类型?

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

感谢您的进一步回复。

更新 #2:

首先,感谢您的耐心等待……

不幸的是,在 VS2013 中,我没有找到任何“resolve”函数来显示我丢失的使用 - 但我发现了使用我自己的丢失 :-)
同时我添加了 使用 System.Reflection;

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

已解决并编译无误

此外,我已将 .jpg 的类型从“Android 资源”更改为“嵌入式资源”。
然后应用程序崩溃: 长长度 = s.Length; 好像 s 为空(我认为找不到 jpg)

进一步 - 如果我将类型更改为“嵌入式资源” - Xamarin.Forms (Avatar.Source = "SymbolMann.jpg";) 不再找到该图像

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

那么……资源的正确类型是什么(以便可以使用 assembly.GetManifestResourceStream() 加载它?

我是否必须在文件名 (SymbolMann.jpg) 中添加一些内容才能找到它?

我必须如何更改 Avatar.Source = "SymbolMann.jpg" 才能找到它(如果我将类型从“Android Resource”更改为其他任何内容)?

我的再次需求:
在注册页面上,默认图像(符号)在标准 Xamarin.Forms 图像 (avatar.source = "SymbolMann.jpg" / "SymbolFrau.jpg") 中显示为男性和女性的头像。
.jpg 存储在每个项目(Android、iOS 和 WP)的标准目录中,可以从图像对象毫无问题地访问它们。
然后,用户可以获取默认图像之一或通过 mediapicker 加载另一个图像。
如果用户点击“注册”按钮,我必须从图像创建一个字节数组,以通过 json 将其发送到我们的网络服务。
如果用户通过 mediapicker 选择另一个图像,我有权访问流,问题是,从 default-images 变成一个字节数组(在每个平台上)。

再次感谢您的帮助...

【问题讨论】:

    标签: c# image xamarin xamarin.forms


    【解决方案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 中的文本编辑器无法识别某些内容时,它会用红色突出显示它。右键单击它会在上下文菜单中为您提供“解决”选项:

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

    更多关于 Xamarin 表单中的图像here

    【讨论】:

    • 嗨 Dimitris 我想,我已经找到了解决方案。如果我确定我可以在所有平台上工作,我会在这里发布解决方案。非常感谢 - 你引导我朝着正确的方向前进 :-)
    【解决方案2】:

    感谢@FredWenger 和@Dimitris - 这是我根据您的回答提出的解决方案:

    1. 将您的图像添加到 Xamarin Forms (Portable) 项目中您喜欢的任何位置。我只是 追根究底:MyProject\DaffyDuck.jpg
    2. 将图像标记为Embedded Resource(文件属性 -> 构建 行动)
    3. 调用引用嵌入式资源的函数,如下所示: ImageDataFromResource("MyProject.DaffyDuck.jpg")
      请注意,您需要包含项目名称才能获取程序集名称 *。
    4. 这是函数:

      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;
      }
      
    5. 如果您需要在 ImageView 控件中引用加载的数据,请执行 因此:
      ImageView.Source = ImageSource.FromStream(() => new MemoryStream(imageData));

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

    【讨论】:

    • 我不知道为什么 OP 选择走更长的路,也许是旧版本的 XF,但你的答案是今天最短和最好的解决方案。
    • 这是一个非常有用的总结——谢谢!特别是关于嵌入式资源和 assembly.GetManifestResourceNames() 的注释 - 你是一个传奇
    • 不敢相信我在 2016 年找到了明确发布的解决方案,但我们到了。
    【解决方案3】:

    首先,非常感谢 Dimitris 引导我走向正确的方向! 

    结论:

    基地:
    我使用 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”。
    这没有问题。

    问题:
    由于用户数据(包括头像)存储在 SQL Server 上(通过 Json-web 服务),我必须将图像数据转换为字节数组(然后将其与其他数据作为字符串一起发送)到网络服务)。
    问题是,从默认目录加载图像并将其转换为字节数组(其中 文件访问是问题 -> 无法访问)。

    解决办法:
    感谢 Dimitris,引导我朝着正确的方向前进,我已经实施了以下解决方案:

    在所有项目(Android、iOS 和 WP)中,我添加了一个新目录“\Embedded\”(直接在根目录下)。 然后,我在此目录中复制了我的两个 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 只是一个全局变量。
    要检查命名空间名称,我必须右键单击每个平台的项目并查看已确定的命名空间名称(在您的项目中可能会有所不同)。
    .嵌入式。是新创建的目录(所有平台上的名称相同)。

    此外,我还为男性图像 (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
    

    然后我在另一个字节数组中接管选定的字节数组(与显示图像平行)(以便以后作为字符串上传到网络服务)

    AvatarErfassung = GV.ByteSymbolMann;
    

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

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

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

    此解决方案适用于所有平台(Android、iOS、WP)。

    我希望这可以帮助其他一些用户……

    最后……感谢 Dimitris

    【讨论】:

      【解决方案4】:

      添加到noelicus's answer:

      设置

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

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

      代码

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

      【讨论】:

        【解决方案5】:

        我在让它工作时遇到了很多问题。我的项目已经在其他地方使用 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,但你会很高兴你这样做了。

        【讨论】:

          【解决方案6】:

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

          1. 将图像存储在 PCL 的根文件夹中。
          2. 在属性中将其构建操作设为嵌入式资源。
          3. 然后打印<b><i>var names = assembly.GetManifestResourceNames()</i></b>数组,这将是一个字符串数组,可以使用

            打印
            foreach(var data in names){Debug.WriteLine(data);}
            
          4. 在那里你会看到图像的资源名称,然后使用方法

            Stream s = assembly.GetManifestResourceStream("resource_name_you_found.format")
            

          【讨论】:

          • 虽然如果您不知道资源名称,这是查找资源名称的好信息,但我认为这不能解决问题,这本质上是如何将图像转换为字节数组
          猜你喜欢
          • 2012-06-04
          • 1970-01-01
          • 2014-07-31
          • 2016-09-20
          • 2011-05-28
          • 1970-01-01
          • 2013-08-27
          • 2015-08-19
          • 2012-07-11
          相关资源
          最近更新 更多