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

Xamarin.Forms: How to load an image from Resources into a byte-array?

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

我使用 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”作为项目资源包含在内(在每个项目中)并显示在页面上(没有问题)。
我现在想将图像放入一个字节数组中以将其发送到我们的网络服务。 我没有找到任何方法来访问图像 "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是null(我觉得是找不到jpg)

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

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

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

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

如何更改 Avatar.Source = "SymbolMann.jpg" 以便找到它(如果我将类型从“Android 资源”更改为其他类型)?

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

再次感谢您的帮助...

您可以通过从程序集中读取资源流轻松地做到这一点:

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 关键字 return 的内容)。另一种方法是对项目中包含的任何对象调用 GetType() 方法。

请注意,如果您对项目中未包含的类型调用 typeof(X),您将无法获得正确的程序集。例如。调用 typeof(ContentPage).GetTypeInfo().Assembly,会 return ContentPage 类型所在的程序集。 而不是 包含您的资源的程序集。我希望这不会造成混淆。如果是,请告诉我。

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

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

有关 Xamarin Forms 图像的更多信息 here

首先,非常感谢Dimitris,他指引了我正确的方向! 

结论:

基数:
我使用 VS2013 - 更新 2,Xamarin.Forms (1.4.1-Pre1),我的应用程序基于模板 “空白应用程序(Xamarin.Forms 共享)” (不是 PCL)并且我使用所有平台(Android、iOS、WP)。

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

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

解决方法:
感谢 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

然后我在另一个字节数组中接管 selected 字节数组(与显示图像并行)(稍后作为字符串上传到网络服务)

AvatarErfassung = GV.ByteSymbolMann;

如果用户更改select离子(例如更改为女性):

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

注意:如果用户 select 来自 mediapicker 的另一个图像,我可以直接访问流,所以这从来都不是问题。

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

希望这对其他用户有帮助…

还有...最后感谢 Dimitris

感谢@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));

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

我使用 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")
    

我在让它工作时遇到了很多问题。我的项目已经在别处使用 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,但您会很高兴自己安装了。

添加到

设置

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

  2. 在Visual Studio中右击图片文件然后selectBuild 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;
    }
}