Xamarin.Forms:如何从参考资料中加载图像到字节数组中?

kadbb459  于 2022-12-07  发布在  其他
关注(0)|答案(6)|浏览(121)

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. 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).
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.

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

Hi Dimitris
Thanks for your (fast) replay.
As I wrote, I work with Xamarin.Forms.
The images are stored as resources: in \Resources\Drawable\ (for Android), \Resources\ (for iOS) and in the root (for 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?)”

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.

What do I have to place as type?
= typeof(????).GetTypeInfo().Assembly:
Thanks for a further reply.

Update #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 :-)
In the meantime I have added using System.Reflection;
Now, the line: 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”.
The App then crashes in the line: long length = s.Length; as it seems that s is null (I think the jpg is not found)
Further - if I change the type to “Embedded Ressource” - the image is not found any more by Xamarin.Forms (Avatar.Source = "SymbolMann.jpg";)
If I change the type of the .jpg to “Compile” I can’t compile the app.
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()?
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)?

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”).
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.
The user then can take one of the default-images or load another one over 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.
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).
Once again thanks for your help...

0ve6wy6x

0ve6wy6x1#

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

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. Hence, typeof(X) , where "X" is any type in your assembly. For example, if you are using a custom page called MyCustomPage :

public class MyCustomPage : ContentPage {}

... this is what you would pass: typeof(MyCustomPage)
You need any instance of type Type (this is what the typeof keyword returns basically). The alternative is to call the GetType() method on any object that is included in your project.
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. 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. 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. When the text editor in Xamarin Studio does not recognize something, it highlights it with red. Right-clicking on that will give you a Resolve option in the context menu:

If the type or method can be resolved, it will show that option.
More on images in Xamarin Forms here .

nszi6y05

nszi6y052#

Thanks @FredWenger and @Dimitris - here's my solution based on both your answers:

  1. Add your image to the Xamarin Forms (Portable) project wherever you like. I just went for the root: MyProject\DaffyDuck.jpg
  2. Mark the image as an Embedded Resource (file properties -> build action)
  3. Call the function referencing the Embedded Resource like this: 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;
}
  1. If you need to reference the loaded data in an ImageView control do it thus:
    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(); and see what is listed in your project.
rm5edbpk

rm5edbpk3#

    • 首先,非常感谢Dimitris,他引导我走上了正确的方向!**
    • 结论:**
    • 基地:**

我使用VS2013-update 2,Xamarin.Forms(1.4.1-Pre1),我的应用程序基于模板**"空白应用程序(Xamarin.Forms共享)"(非PCL),我使用所有平台(Android,iOS,WP)。
在用户注册页面上,用户可以选择一个图像作为头像(随后显示在他的帖子中)。根据用户的性别,显示了两个默认图像(一个用于男性,一个用于女性)。然后,用户可以超越其中一个默认图像或从媒体选择器中选择另一个图像。
默认图像存储在内容图像的默认位置(对于Android,例如,在
/Resources/Drawable/**下)。
图像显示在XF标准图像对象中,图像源="xxx.jpg"。
这已经没有问题了。

    • 问题:**

由于用户数据(包括头像)存储在SQL服务器上(通过Json-Web服务),我必须将图像数据转换为字节数组(然后将其与其他数据一起作为字符串发送到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只是一个全局变量。
为了 checkout 命名空间名称,我必须右键单击每个平台的项目,然后查看已确定的命名空间名称(在您的项目中可能会有所不同)。
.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加载到image-object中的代码,我不得不从以下代码进行更改:

//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选择另一个图像,我可以直接访问流,所以这从来不是问题所在。

    • 此解决方案适用于所有平台(Android、iOS、WP)。**
    • 我希望这可以帮助一些其他用户...**

"最后感谢迪米特里斯"

guykilcj

guykilcj4#

Adding on to noelicus's answer :

Setup

  1. Add your image to the root folder of the Xamarin.Forms project, e.g. MyProject\pizza.png
  2. In Visual Studio, right-click on the image file and select 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;
    }
}
velaa5lx

velaa5lx5#

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

7kjnsjlb

7kjnsjlb6#

我使用noelicus给出的方法打印ManifestResourceNames数组中的所有名称,我发现我的图像名称是ProjectName.Droid.image_name.jpeg。然后我把方法调用从assembly.GetManifestResourceStream("image_name.jpg")改为assembly.GetManifestResourceStream("ProjectName.Droid.image_name.jpeg")。它工作了。所以正确的答案是:
1.将图像存储在PCL的根文件夹中。
1.使其生成操作在属性中作为嵌入资源
1.然后打印<b><i>var names = assembly.GetManifestResourceNames()</i></b>数组,它将是一个字符串数组,可以使用

foreach(var data in names){Debug.WriteLine(data);}

1.在那里,您将看到image的资源名称,然后使用方法

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

相关问题