I want to load an image from a URL in the server side, and use the await syntax if possible.
In a WPF application I could do that:
var image = new BitmapImage(new Uri("http://..."));
image.ImageOpened += (s, e) =>
{
}
In MVC, instead, I have the by default System.Drawing.Bitmap which dose not have an option to load from a URL.
Should I :
Add reference to System.Windows.Media.Imaging and use BitmapImage
class wrapping it with TaskCompletionSource.
Somehow load the image using the Bitmap class.
A working code using one of the above with async/wait would be great.
Should be quite simple:
private Task<Stream> GetStreamAsync()
{
var httpClient = new HttpClient();
return httpClient.GetStreamAsync(#"http://urlhere");
}
and call it:
private async Task GetImageAndDoStuff()
{
Stream imageStream = await GetStreamAsync();
using (Bitmap bitmap = new Bitmap(Image.FromStream(imageStream)))
{
}
}
Related
I'm trying to write a function which accepts my ICanvasEffect as a parameter (which in my case is a Win2D BlendEffect), and I want to convert the CanvasRenderTarget to a BitmapImage so that I can use it in a UWP Image control:
private async Task<BitmapImage> GetBitmapImage(CancellationToken ct, ICanvasImage effect)
{
using (var target = new CanvasRenderTarget(CanvasDevice.GetSharedDevice(), 320f, 240f, 96))
{
using (var ds = target.CreateDrawingSession())
{
// Draw the image with the supplied ICanvasImage
ds.DrawImage(effect);
}
//await target.SaveAsync(outputStream, CanvasBitmapFileFormat.Jpeg).AsTask(ct);
}
}
As you see in the commented code, CanvasRenderTarget has a SaveAsync method I can use to save it to a Stream, but how?
Figured it out:
using (var stream = new InMemoryRandomAccessStream())
{
await target.SaveAsync(stream, CanvasBitmapFileFormat.Jpeg).AsTask(ct);
var bmp = new BitmapImage();
stream.Seek(0);
await bmp.SetSourceAsync(stream);
}
As you see in the commented code, CanvasRenderTarget has a SaveAsync method I can use to save it to a Stream, but how?
If you want to save it to stream, you could refer to Win2D samples.
There's many samples show that how to use CanvasRenderTarget.SaveAsync() method.
For example, Win2D-Samples/ExampleGallery/Infrastructure/AppIconGenerator.cs#L217
I am trying to get an image's dimensions before I display it in the OnLoaded() method. I want to do this synchronously (not asynchronously) because I need to know the image's dimensions before the program continues. I'm making a mapping program where the coordinates, scale and translation offset of the background image are important and are all dependent on knowing the image's dimensions from the very beginning of the app.
I have read most of the msdn documentation on StorageFile, SoftwareBitmap, and asynchronous method calls along with trying many methods I have found online with no success.
https://msdn.microsoft.com/en-us/windows/uwp/threading-async/asynchronous-programming-universal-windows-platform-apps
https://msdn.microsoft.com/en-us/windows/uwp/threading-async/call-asynchronous-apis-in-csharp-or-visual-basic
Basically I am looking for a synchronous c# function that takes the uri of an image file and returns a Point containing the dimensions. i.e.
public Point getImageDimensions(Uri u){...}
This function is so easy to implement in Android apps, I don't understand why it is so difficult in UWP apps.
I can provide more details if needed, thanks in advance.
I get the following error with this code:
Exception thrown: 'System.Exception' in App1.exe
Exception thrown: 'System.AggregateException' in mscorlib.ni.dll
App1.xaml.cs
using System;
using System.Diagnostics;
using System.Threading.Tasks;
using Windows.Foundation;
using Windows.Graphics.Imaging;
using Windows.Storage;
using Windows.Storage.Streams;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Imaging;
using Windows.UI.Xaml.Shapes;
namespace App1
{
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
this.Loaded += OnLoaded;
Point p = Task.Run(() => getImageDimensions("ms-appx:///Assets/NewSpaceBackground.png")).Result;
Debug.WriteLine("p.X: " + p.X + " p.Y: " + p.Y);
}
void OnLoaded(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
Rectangle backgroundRect1 = new Rectangle();
ImageBrush imgBrushBackground1 = new ImageBrush();
imgBrushBackground1.ImageSource = new BitmapImage(new Uri(#"ms-appx:///Assets/NewSpaceBackground.png"));
backgroundRect1.Fill = imgBrushBackground1;
//backgroundMap.Add(backgroundRect1);
}
public async Task<Point> getImageDimensions(string strURI)
{
Point p;
StorageFile photo;
try
{
var uri = new Uri(strURI);
photo = await StorageFile.GetFileFromApplicationUriAsync(uri);
}
catch (Exception e)
{
Debug.WriteLine(e.StackTrace);
return p;
}
//read in the bitmap stream
IRandomAccessStream stream = await photo.OpenAsync(FileAccessMode.Read);
BitmapDecoder decoder = await BitmapDecoder.CreateAsync(stream);
SoftwareBitmap softwareBitmap = await decoder.GetSoftwareBitmapAsync();
//convert the image to a bitmap
SoftwareBitmap softwareBitmapBGR8 = SoftwareBitmap.Convert(softwareBitmap, BitmapPixelFormat.Bgra8, BitmapAlphaMode.Premultiplied);
SoftwareBitmapSource bitmapSource = new SoftwareBitmapSource();
await bitmapSource.SetBitmapAsync(softwareBitmapBGR8);
//print out the dimensions
Debug.WriteLine(softwareBitmapBGR8.PixelWidth + " " + softwareBitmapBGR8.PixelHeight);
p.X = softwareBitmapBGR8.PixelWidth;
p.Y = softwareBitmapBGR8.PixelHeight;
return p;
}
}
}
I am trying to get an image's dimensions before I display it in the OnLoaded() method. I want to do this synchronously (not asynchronously) because I need to know the image's dimensions before the program continues.
There are some problems when you want to call async functions in constructor, for details you can refer to Stephen Cleary's blog: Async OOP 2: Constructors.
In UWP, You can put the Page initialization codes (getImageDimensions and the codes of displaying the image) in Page.OnNavigatedTo method and use await in it:
protected override async void OnNavigatedTo(NavigationEventArgs e)
{
Point p = await getImageDimensions("ms-appx:///Assets/NewSpaceBackground.png");
Debug.WriteLine("p.X: " + p.X + " p.Y: " + p.Y);
Rectangle backgroundRect1 = new Rectangle();
ImageBrush imgBrushBackground1 = new ImageBrush();
imgBrushBackground1.ImageSource = new BitmapImage(new Uri(#"ms-appx:///Images/image03.jpg"));
backgroundRect1.Fill = imgBrushBackground1;
...
}
Update:
Would you recommend an easier, synchronous, way to get the dimensions?
Most of the file related operations are designed to be async, user can use/not use await to choose to do the operation sychronously or asynchronously.
There is indeed an easy way to obtain the image's dimension:
StorageFile file = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///images/image01.png"));
var properties=await file.Properties.GetImagePropertiesAsync();
var width = properties.Width;
var height = properties.Height;
Instead of streaming it into a Bitmap, could you load it into a System.Drawing.Image, like so?:
System.Drawing.Image img = System.Drawing.Image.FromStream(myStream);
Then you have access to the System.Drawing.Image's Size property, which should give you what you need, as well as easy access to resizing. All of this is available in .NET Core and UWP if memory serves.
I have a ListView of multiple items and each item in the list should have an image associated with it. I have created an Array Adapter to hold each list item and have the url of the image I wish to load.
I am trying to asynchronously load the image using a web request and have it set the image and update it in the view once it has been loaded but the view should render immediatly with a default image.
Here is the method I am using to try to load the image
private async Task<Bitmap> GetImageBitmapFromUrl(string url, ImageView imageView)
{
Bitmap imageBitmap = null;
try
{
var uri = new Uri(url);
var response = httpClient.GetAsync(uri).Result;
if (response.IsSuccessStatusCode)
{
var imageBytes = await response.Content.ReadAsByteArrayAsync();
if (imageBytes != null && imageBytes.Length > 0)
{
imageBitmap = BitmapFactory.DecodeByteArray(imageBytes, 0, imageBytes.Length);
imageView.SetImageBitmap(imageBitmap);
}
}
}
catch (Exception e)
{
Console.WriteLine(e);
}
return imageBitmap;
}
I am initializing my HttpClient like so:
httpClient = new HttpClient();
httpClient.Timeout = new TimeSpan(0, 0, 10);
httpClient.MaxResponseContentBufferSize = 256000;
And here is how I am calling the GetImageFromUrl Method
ImageView myImage = convertView.FindViewById<ImageView>(Resource.Id.AsyncImageView)
myImage.SetImageBitmap(null);
string url = GetItem(position);
GetImageBitmapFromUrl(url, myImage);
Using this code the request eventually comes back with a bad request but when I view the url I am using and paste it into a browser window it brings up the image I am expecting.
You don't need to care about downloading images.
There exist two good libraries to load an image into a ImageView async in Xamarin.Android.
Picasso component (source) usage:
Picasso.With(context)
.Load("http://i.imgur.com/DvpvklR.png")
.Into(imageView);
FFImageLoading (source) usage:
ImageService.Instance.LoadUrl(urlToImage).Into(_imageView);
Note (from documentation):
Unlike Picasso you cannot use FFImageLoading with standard ImageViews.
Instead simply load your images into ImageViewAsync instances.
Updating your code is very easy since ImageViewAsync inherits from
ImageView.
So my app gets a BitmapImage from a url, which works, and I then need to assign that image to a writeablebitmap. For the moment I have this:
Debug.WriteLine("Poster opened for loading. Url=" + e);
BitmapImage img = new BitmapImage(new Uri(e));
Backdrop.Source = img;
img.ImageOpened += async (s, e1) =>
{
WriteableBitmap wbm = new WriteableBitmap(((BitmapImage)s).PixelWidth, ((BitmapImage)s).PixelHeight);
var storageFile = await StorageFile.GetFileFromApplicationUriAsync(new Uri(e));
using (var stream = await storageFile.OpenReadAsync())
{
await wbm.SetSourceAsync(stream);
}
};
Which is really awful code, but it all works until StorageFile.GetFileFromApplicationUriAsync(), where I'm told the Value falls within an unexpected range. I'm guessing this is because it only acceps the "ms-appx:///" format, and not "http://". Is there any way to get this working? And eventually in a cleaner way?
I thought this would be easy, but it's a nightmare. I've been trying to do this since yesterday.
P.S. I know this might appear like a duplicate, but I have found absolutely nothing on StackOverflow that actually works.
Try this:
var httpClient = new HttpClient();
var buffer = await httpClient.GetBufferAsync(pictureUri);
var writeableBitmap = new WriteableBitmap(width, height);
using (var stream = buffer.AsStream())
{
await writeableBitmap.SetSourceAsync(stream.AsRandomAccessStream());
}
I get image URL at runtime from the xml file reader. This image URL is pass to the below method to download it dynamically.
public void Display_Image(string MyURL)
{
BitmapImage bi = new BitmapImage();
bi.UriSource = new Uri(this.BaseUri, MyURL);
Img_Poster.Source = bi;
}
But this doesn't work. I don't get any image source. The above code works fine with static URL provided at compile time. What do I need to do more?
The method I suggested below is obsolete. However, creating a new Bitmap image created dynamically with a Uri determined at runtime IS supported and working on the RTM build of Windows 8. Display_Image(url) should work as you expect.
You can get the image stream using the CreateFromUri helper: http://msdn.microsoft.com/en-us/library/windows/apps/windows.storage.streams.streamreference.createfromuri.aspx#Y0
var stream = RandomAccessStreamReference.CreateFromUri(new Uri(imageUrl))
You should then be able to set the source of your bitmap to the RandomAccessStream that the helper returns
I've had similar problems with previously-working Bitmap code not working on Windows RT, an early attempt convinces me that it refuses to download anything unless it is going to be displayed on the UI (here, I needed to insert a 1ms delay before assigning sources just to get it to trigger the image download):
var image = .... // reference to animage on the UI
var placeholder = ... // a placeholder BitmapImage
var source = ... // uri to download
image.Source = placeholder;
var src = new BitmapImage(new Uri(source));
src.ImageOpened += (s, e) =>
{
var bi = s as BitmapImage;
image.Source = bi;
};
image.Source = src;
// Delay required to trigger download
await Task.Delay(1);
image.Source = placeholder;
Here's another solution I've tried with success:
var image = .... // reference to animage on the UI
var source = ... // uri to download
var placeholder = ... // a placeholder BitmapImage
image.Source = placeholder;
var bytes = await new HttpClient().GetByteArrayAsync(source);
var img = new BitmapImage();
await img.SetSourceAsync(bytes.AsBuffer().AsStream().AsRandomAccessStream());
image.Source = img;