简体   繁体   English

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

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

I have a (hopefully) simple question (I don't have found an answer, that fit's by all of my searches).我有一个(希望如此)简单的问题(我没有找到适合我所有搜索的答案)。

I work with Xamarin.Forms 1.4.1-Pre-1.我使用 Xamarin.Forms 1.4.1-Pre-1。 In my app, I have:在我的应用程序中,我有:

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

where the image "SymbolMann.jpg” is included as project-resource (in each project) and showed on a page (without problems).其中图像“SymbolMann.jpg”作为项目资源(在每个项目中)包含并显示在页面上(没有问题)。
I now want to put the image in a byte-array to send it to our webservice.我现在想将图像放入字节数组中以将其发送到我们的网络服务。 I don't have found any way to access the image "SymbolMann.jpg" (to load it in a byte-array) or to use (however) the Avatar.Source therefore.我没有找到任何方法来访问图像“SymbolMann.jpg”(将其加载到字节数组中)或因此使用(但是)Avatar.Source。

Question:题:
How to get the image “SymbolMann.jpg” into the byte-array “AvatarErfassung” ?如何将图像“SymbolMann.jpg”放入字节数组“AvatarErfassung”中?

Thanks for every answer谢谢你的每一个回答

Hi Dimitris嗨迪米特里斯

Thanks for your (fast) replay.感谢您的(快速)重播。
As I wrote, I work with Xamarin.正如我所写,我与 Xamarin 一起工作。 Forms .表格
The images are stored as resources : in \\Resources\\Drawable\\ (for Android), \\Resources\\ (for iOS) and in the root (for WP).图像存储为资源:在\\Resources\\Drawable\\(适用于Android)、\\Resources\\(适用于iOS)和根目录中(适用于WP)。 I have to load the image on a content-page.我必须在内容页面上加载图像。

If I overtake your code, to the line:如果我超过您的代码,请转到以下行:

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

I have the error-message (translated to English):我有错误信息(翻译成英文):
“System.Type don't contain a definition for GetTypeInfo method (is a using missing?)” “System.Type 不包含 GetTypeInfo 方法的定义(是否缺少 using?)”

Do I have to add a specific using?我必须添加特定的用途吗?

You write: // you can replace "this.GetType()" with "typeof(MyType)", where MyType is any type in your assembly.你写: // 你可以用“typeof(MyType)”替换“this.GetType()”,其中 MyType 是你的程序集中的任何类型。

What do I have to place as type?我必须放置什么类型?

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

Thanks for a further reply.感谢您的进一步答复。

Update #2:更新#2:

Firts, thanks for your patience…首先,感谢您的耐心等待……

Unfortunately in VS2013 , I don't find any function “resolve” that shows me the missing using - but I have find out the missing using my self :-)不幸的是,在VS2013 中,我没有找到任何“解决”功能显示我丢失的使用 - 但我已经使用我自己找到了丢失的东西:-)
In the meantime I have added using System.Reflection;与此同时,我添加了使用 System.Reflection;

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

is resolved and compiled without error已解决并编译无错误

Further, I have changed the type of the .jpg from “Android Resource” to “Embedded Ressource”.此外,我已将 .jpg 的类型从“Android Resource”更改为“Embedded Ressource”。
The App then crashes in the line: long length = s.Length;然后应用程序在行中崩溃: long length = s.Length; as it seems that s is null (I think the jpg is not found)因为似乎 s 为空(我认为找不到 jpg)

Further - if I change the type to “Embedded Ressource” - the image is not found any more by Xamarin.Forms (Avatar.Source = "SymbolMann.jpg";)此外 - 如果我将类型更改为“嵌入式资源” - Xamarin.Forms(Avatar.Source =“SymbolMann.jpg”;)不再找到该图像

If I change the type of the .jpg to “Compile” I can't compile the app.如果我将 .jpg 的类型更改为“编译”,我将无法编译该应用程序。
Error-message: A namespace can't contain directly members like fields or methods.错误消息:命名空间不能直接包含字段或方法等成员。

So… what is the correct type to the resource (so that it can be loaded with assembly.GetManifestResourceStream()?那么……资源的正确类型是什么(以便它可以使用 assembly.GetManifestResourceStream() 加载?

Do I have to add something to the filename (SymbolMann.jpg) so that it will be found?我是否必须在文件名 (SymbolMann.jpg) 中添加一些内容才能找到它?

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)?我如何更改 Avatar.Source = "SymbolMann.jpg" 以便找到它(如果我将类型从“Android Resource”更改为其他任何内容)?

Once again my needs:再次我的需求:
On a registration-page, default-images (symbols) are showed as avatar for man and woman in a standard-Xamarin.Forms image (avatar.source = “SymbolMann.jpg” / “SymbolFrau.jpg”).在注册页面上,默认图像(符号)在标准 Xamarin.Forms 图像(avatar.source = “SymbolMann.jpg”/“SymbolFrau.jpg”)中显示为男性和女性的头像。
The .jpg's are stored in the standard-directories for each project (Android, iOS and WP) where the are accessible without problems from the image-object. .jpg 存储在每个项目(Android、iOS 和 WP)的标准目录中,可以从图像对象访问这些文件而不会出现问题。
The user then can take one of the default-images or load another one over mediapicker.然后,用户可以使用默认图像之一或通过 mediapicker 加载另一个。
If the user the tap the button “Register”, I have to create a byte-array from the Image to send it via json to our webservice.如果用户点击“注册”按钮,我必须从图像创建一个字节数组,以通过 json 将其发送到我们的网络服务。
If the user select another image via mediapicker, I have access to the stream, the problem is, to become a byte-array from the default-images (in every platform).如果用户通过 mediapicker 选择另一个图像,我可以访问流,问题是,从默认图像(在每个平台中)变成一个字节数组。

Once again thanks for your help...再次感谢您的帮助...

You can easily do this, by reading the resource stream from the assembly:通过从程序集中读取资源流,您可以轻松地做到这一点:

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

EDIT:编辑:

To get the assembly that holds the embedded resources of your project you need to call the GetTypeInfo method on a type included in that assembly.要获取包含项目嵌入资源的程序集,您需要对该程序集中包含的类型调用GetTypeInfo方法。 Hence, typeof(X) , where "X" is any type in your assembly.因此, typeof(X) ,其中“X”是程序集中的任何类型。 For example, if you are using a custom page called MyCustomPage :例如,如果您使用名为MyCustomPage的自定义页面:

public class MyCustomPage : ContentPage {}

... this is what you would pass: typeof(MyCustomPage) ...这就是您要传递的内容: typeof(MyCustomPage)

You need any instance of type Type (this is what the typeof keyword returns basically).您需要Type任何实例(这是typeof关键字返回的基本内容)。 The alternative is to call the GetType() method on any object that is included in your project.另一种方法是对项目中包含的任何对象调用GetType()方法。

Note that you will not be able to get the correct assembly if you call typeof(X) on a type that is not included in your project.请注意,如果您对未包含在您的项目中的类型调用typeof(X) ,您将无法获得正确的程序集。 Eg.例如。 calling typeof(ContentPage).GetTypeInfo().Assembly , would return the assembly that the ContentPage type resides in. Which is not the one that includes your resources.调用typeof(ContentPage).GetTypeInfo().Assembly ,将返回ContentPage类型所在的程序集。这不是包含您的资源的程序集。 I hope this is not confusing.我希望这不会令人困惑。 If it is, please let me know.如果是,请告诉我。

Now, the GetTypeInfo method is an extension method included in the System.Reflection namespace.现在, GetTypeInfo方法是包含在System.Reflection命名空间中的扩展方法。 When the text editor in Xamarin Studio does not recognize something, it highlights it with red.当 Xamarin Studio 中的文本编辑器无法识别某些内容时,它会以红色突出显示。 Right-clicking on that will give you a Resolve option in the context menu:右键单击它会在上下文菜单中为您提供一个解决选项:

在 Xamarin Studio 中右键单击

If the type or method can be resolved, it will show that option.如果可以解析类型或方法,它将显示该选项。

More on images in Xamarin Forms here .有关 Xamarin Forms 中图像的更多信息,请访问此处

Thanks @FredWenger and @Dimitris - here's my solution based on both your answers:谢谢@FredWenger 和@Dimitris - 这是我基于你的回答的解决方案:

  1. Add your image to the Xamarin Forms (Portable) project wherever you like.随时随地将图像添加到 Xamarin Forms(便携式)项目。 I just went for the root: MyProject\\DaffyDuck.jpg我只是去了根: MyProject\\DaffyDuck.jpg
  2. Mark the image as an Embedded Resource (file properties -> build action)将图像标记为Embedded Resource (文件属性 -> 构建操作)
  3. Call the function referencing the Embedded Resource like this: ImageDataFromResource("MyProject.DaffyDuck.jpg")像这样调用引用嵌入式资源的函数: ImageDataFromResource("MyProject.DaffyDuck.jpg")
    Notice that you need to include the project name to get the assembly name *.请注意,您需要包含项目名称才能获得程序集名称 *.
  4. This is the function:这是函数:

     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. If you need to reference the loaded data in an ImageView control do it thus:如果您需要在 ImageView 控件中引用加载的数据,请执行以下操作:
    ImageView.Source = ImageSource.FromStream(() => new MemoryStream(imageData));

*NB: If step 3 doesn't work and you want to check your embedded resource names call this: var names = assembly.GetManifestResourceNames(); *注意:如果第 3 步不起作用,并且您想检查嵌入的资源名称,请调用: var names = assembly.GetManifestResourceNames(); and see what is listed in your project.并查看您的项目中列出的内容。

First, many thanks to Dimitris who has guided me to the right direction!首先,非常感谢 Dimitris,他引导我走向正确的方向!

Conclusion:结论:

The base:基地:
I work with VS2013 - update 2, Xamarin.Forms (1.4.1-Pre1) and my app is based on the template “Blank App (Xamarin.Forms Shared)” (not PCL) and I use all platforms (Android, iOS, WP).我使用 VS2013 - 更新 2、Xamarin.Forms (1.4.1-Pre1) 并且我的应用程序基于模板“Blank App (Xamarin.Forms Shared)” (不是 PCL)并且我使用所有平台(Android、iOS、可湿性粉剂)。

On a user-registration-page the user can select an image as avatar (that then later is showed to his postings).在用户注册页面上,用户可以选择一个图像作为头像(然后显示在他的帖子中)。 There are two default-images (one for male one for female), that are showed, depending of the sex of the user.根据用户的性别,显示了两个默认图像(一个是男性,一个是女性)。 The user then can overtake one of the default-images or select another image from the media-picker.然后用户可以取代默认图像之一或从媒体选择器中选择另一个图像。
The default-images were stored on the default-locations for content-images (for Android eg under /Resources/Drawable/ ).默认图像存储在内容图像的默认位置(对于 Android,例如在/Resources/Drawable/ 下)。
The images were showed in a XF-Standard-Image-Object with Image-Source = “xxx.jpg”.图像显示在 XF-Standard-Image-Object 中,Image-Source = “xxx.jpg”。
This has worked without problems.这没有问题。

The problem:问题:
As the user-data (including avatar) are stored on a SQL-Server (over a Json-web-service), I have to convert the image-data to a byte-array (to send it then with the other data as string to the web-service).由于用户数据(包括头像)存储在 SQL 服务器上(通过 Json 网络服务),我必须将图像数据转换为字节数组(然后将其与其他数据作为字符串发送到网络服务)。
The problem was, to load the images from the default-directories and convert it to a byte-array (whereby the file-access was the problem -> not reachable).问题是,从默认目录加载图像并将其转换为字节数组(文件访问是问题-> 无法访问)。

The solution:解决方案:
Thanks Dimitris, that have guided me in the correct direction, I have implemented the following solution:感谢 Dimitris,他引导我朝着正确的方向前进,我已经实施了以下解决方案:

In all projects (Android, iOS and WP), I have added a new directory “\\Embedded\\” (directly under the root) .在所有项目(Android、iOS 和 WP)中,我添加了一个新目录“\\Embedded\\”(直接在根目录下) Then, I have copied my two jpg's (“SymbolMann.jpg” and "SymbolFrau.jpg”) in this directory (for all Platforms).然后,我将我的两个 jpg(“SymbolMann.jpg”和“SymbolFrau.jpg”)复制到此目录中(适用于所有平台)。
Then, I have changed the resource-type to the images from eg “AndroidResource” to “Embedded Resource” (for all platforms the same type).然后,我将资源类型更改为图像,例如从“AndroidResource”更改为“Embedded Resource” (对于所有平台都相同类型)。

In my Startup-Code to the app (app.cs), I have added:在应用程序(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.";

where GV.cEmbeddedAblage is simply a global variable.其中 GV.cEmbeddedAblage 只是一个全局变量。
To checkout the namespace-names, I had to right-click the projects for each platform and have a look at the settled namespace-name (can varies in your project).要检查命名空间名称,我必须右键单击每个平台的项目并查看已确定的命名空间名称(在您的项目中可能会有所不同)。
The .Embedded. .Embedded。 is the new created directory (same name on all platforms).是新创建的目录(所有平台上的名称相同)。

Additionally, I have created global byte-arrays for the male-image (GV.ByteSymbolMann) and for the female-image (GV.ByteSymbolFrau).此外,我为男性形象 (GV.ByteSymbolMann) 和女性形象 (GV.ByteSymbolFrau) 创建了全局字节数组。

In the startup-Code of the registration-page, I have added:在注册页面的启动代码中,我添加了:

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

The code to loading the .jpg into the image-object, I had to change from:将 .jpg 加载到图像对象中的代码,我不得不从:

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

to

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

I then takeover the selected byte-array (parallel to showing the image) in a further byte-array (for later upload to the web-service as string)然后我在另一个字节数组中接管选定的字节数组(与显示图像并行)(稍后作为字符串上传到网络服务)

AvatarErfassung = GV.ByteSymbolMann;

If the user change the selection (eg to woman):如果用户更改选择(例如更改为女性):

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

Note: If the user select another image from mediapicker, I have directly access to the stream, so this was never the problem.注意:如果用户从 mediapicker 选择另一个图像,我可以直接访问流,所以这从来都不是问题。

This solution works for all platforms (Android, iOS, WP).此解决方案适用于所有平台(Android、iOS、WP)。

I hope this may help some other users… 我希望这可以帮助其他一些用户...... 

And... finally thanks to Dimitris还有……最后感谢 Dimitris

Adding on to noelicus's answer :添加到noelicus 的答案

Setup设置

  1. Add your image to the root folder of the Xamarin.Forms project, eg MyProject\\pizza.png将图像添加到 Xamarin.Forms 项目的根文件夹,例如MyProject\\pizza.png

  2. In Visual Studio, right-click on the image file and select Build Action -> EmbeddedResource在 Visual Studio 中,右键单击图像文件并选择Build Action -> EmbeddedResource

在此处输入图片说明

Code代码

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

Example例子

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

I had a lot of problems getting this to work.我在让它工作时遇到了很多问题。 My project was already using FFImageLoading elsewhere, and I found this to be the easiest way to load the .png as a stream:我的项目已经在其他地方使用 FFImageLoading,我发现这是将 .png 作为流加载的最简单方法:

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

Make sure your resource files have the same name in both iOS and Droid projects.确保您的资源文件在 iOS 和 Droid 项目中具有相同的名称。 They'll probably already have their build action set to 'BundleResource' (iOS) or 'AndroidResource' (Droid).他们可能已经将构建操作设置为“BundleResource”(iOS)或“AndroidResource”(Droid)。

Sure, you'll have to install the FFImageLoading Nuget, but you'll be glad you did.当然,您必须安装 FFImageLoading Nuget,但您会很高兴您这样做了。

I used noelicus given idea and printed all the names from ManifestResourceNames array and I found there my image name as ProjectName.Droid.image_name.jpeg .Then I just changed the method call from assembly.GetManifestResourceStream("image_name.jpg") to assembly.GetManifestResourceStream("ProjectName.Droid.image_name.jpeg") .And it worked.我使用了 noelicus 给定的想法并打印了 ManifestResourceNames 数组中的所有名称,我在那里发现我的图像名称为ProjectName.Droid.image_name.jpeg 。然后我只是将方法调用从assembly.GetManifestResourceStream("image_name.jpg")更改为assembly.GetManifestResourceStream("ProjectName.Droid.image_name.jpeg") 。它起作用了。 So the correct answer would be :所以正确答案是:

  1. Store the image in root folder at PCL.将图像存储在 PCL 的根文件夹中。
  2. Make it's build action in properties as Embedded resource.使其在属性中的构建操作作为嵌入式资源。
  3. Then print <b><i>var names = assembly.GetManifestResourceNames()</i></b> array which will be a string array and can be printed using然后打印<b><i>var names = assembly.GetManifestResourceNames()</i></b>数组,这将是一个字符串数组,可以使用打印

    foreach(var data in names){Debug.WriteLine(data);}
  4. There you will see the resource name for image and then use the method在那里您将看到图像的资源名称,然后使用该方法

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM