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.
Related
I'm trying to draw a set of images with SkiaSharp, convert them to byte array, and send to HttpContext stream. For some reason, every image is being displayed in the browser only when the next image has been rendered and sent to the HTTP stream.
After rendering the 1st image, browser shows empty white area, no image
After rendering the 2nd image, browser shows 1st image
After rendering the 3rd image, browser shows 2nd image, and so on
Here is the Web API controller. Uncommenting second drawShapes call will make it work, but I would like to understand why image rendering is delayed. Maybe I just don't call some important method, like Flush or something similar?
Also, this is not an ASP issue, because I tried rendering images with System.Drawing.Common and images were rendered in real time without delay.
Does anybody know what is missing in this method?
[Route("/source")]
public async Task Get()
{
// Initialize Skia
var res = new byte[1000];
var boundary = Guid.NewGuid().ToString();
var generator = new Random();
var map = new SKBitmap(250, 250);
var canvas = new SKCanvas(map);
var inPaint = new SKPaint
{
Color = SKColors.Black,
Style = SKPaintStyle.Fill,
FilterQuality = SKFilterQuality.High
};
var exPaint = new SKPaint
{
Color = SKColors.White,
Style = SKPaintStyle.Fill,
FilterQuality = SKFilterQuality.Low
};
// Create HTTP stream
Response.ContentType = "multipart/x-mixed-replace;boundary=" + boundary;
var outputStream = Response.Body;
var cancellationToken = Request.HttpContext.RequestAborted;
// Create method posting images to HTTP stream
async Task drawShapes()
{
using (var image = SKImage.FromBitmap(map))
{
var pos = generator.Next(50, 150);
canvas.DrawRect(0, 0, 250, 250, exPaint);
canvas.DrawCircle(pos, pos, 20, inPaint);
res = image.Encode(SKEncodedImageFormat.Webp, 100).ToArray();
}
var header = $"--{ boundary }\r\nContent-Type: image/webp\r\nContent-Length: { res.Length }\r\n\r\n";
var headerData = Encoding.ASCII.GetBytes(header);
await outputStream.WriteAsync(headerData);
await outputStream.WriteAsync(res);
await outputStream.WriteAsync(Encoding.ASCII.GetBytes("\r\n"));
}
// Start HTTP stream
await Task.Run(async () =>
{
//while (true)
{
await drawShapes();
//await drawShapes(); // After uncommenting this shows the image (the previous one)
}
});
}
Reproducible sample
This was answered in SkiaSharp discussions.
https://github.com/mono/SkiaSharp/discussions/1975
Possible solutions are to make sure to Encode image only after all drawings are done or to call Encode on bitmap instead of image.
The simplest one is to replace this line.
//res = image.Encode(SKEncodedImageFormat.Webp, 100).ToArray(); // wrong
res = map.Encode(SKEncodedImageFormat.Webp, 100).ToArray(); // correct
coding xamarin form project and also using pcl storage. have problem with saving image or file on device all examples i found show how to save stream of byte array into file but non of them show how to turn convert image into usable format for saving.
var webImage = new Image();
webImage.Source = new UriImageSource
{
CachingEnabled = false,
Uri = new Uri("http://server.com/image.jpg"),
};
byte[] image = ????(what i need)????(webImage.Source);
// get hold of the file system
IFolder folder = rootFolder ?? FileSystem.Current.LocalStorage;
// create a file, overwriting any existing file
IFile file = await folder.CreateFileAsync(fileName, CreationCollisionOption.ReplaceExisting);
// populate the file with image data
using (System.IO.Stream stream = await file.OpenAsync(FileAccess.ReadAndWrite))
{
stream.Write(image, 0, image.Length);
}
In other words looking for code to save image/file on device from url.
Tried ffimageloading since it contain converter to byte[], but always get error:"Object reference not set to an instance of an object." for GetImageAsJpgAsync and GetImageAsPngAsync method. problem might be that image isn't fully loaded. but finish event and finish command never get called even trough image is fully loaded on screen and bound to cachedImage.
var cachedImage = new CachedImage()
{
Source = "https://something.jpg",
FinishCommand = new Command(async () => await TEST()),
};
cachedImage.Finish += CachedImage_Finish;
var image = await cachedImage.GetImageAsJpgAsync();
With FFImageLoading (I noticed you use it):
await ImageService.Instance.LoadUrl("https://something.jpg").AsJpegStream();
If you want to get original file location, use this:
await ImageService.Instance.LoadUrl("https://something.jpg").Success((imageInfo) => { //TODO imageInfo.FilePath } ).DownloadOnly();
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)))
{
}
}
I'm attempting to follow an Windows Phone tutorial, tweaking it a bit to fit my needs. I am trying to grab a new picture every X seconds (30 for debugging) and set it as the new lockscreen background. I created a ScheduleAgent object to call it, however, it seems to be skipping a function. It does not skip this when in the MainPage.xaml.cs, which runs the exact same function.
ScheduleAgent.cs
static ScheduledAgent()
{
....<irrelevant code>....
protected override void OnInvoke(ScheduledTask task)
{
.... < Code went here that shouldn't matter > ....
}
Debug.WriteLine("Imagename = " + imageName);
DownloadImagefromServer("https://s3-us-west-2.amazonaws.com/qoutescreen/0" + imageName + ".jpg");
m.ReleaseMutex();
}
// If debugging is enabled, launch the agent again in one minute.
// debug, so run in every 30 secs
#if(DEBUG_AGENT)
ScheduledActionService.LaunchForTest(task.Name, TimeSpan.FromSeconds(30));
System.Diagnostics.Debug.WriteLine("Periodic task is started again: " + task.Name);
#endif
// Call NotifyComplete to let the system know the agent is done working.
NotifyComplete();
}
//===============================================================================
private async void LockScreenChange(string filePathOfTheImage, bool isAppResource)
{
if (!LockScreenManager.IsProvidedByCurrentApplication)
{
// If you're not the provider, this call will prompt the user for permission.
// Calling RequestAccessAsync from a background agent is not allowed.
await LockScreenManager.RequestAccessAsync();
}
// Only do further work if the access is granted.
if (LockScreenManager.IsProvidedByCurrentApplication)
{
// At this stage, the app is the active lock screen background provider.
// The following code example shows the new URI schema.
// ms-appdata points to the root of the local app data folder.
// ms-appx points to the Local app install folder, to reference resources bundled in the XAP package
var schema = isAppResource ? "ms-appx:///" : "ms-appdata:///Local/";
var uri = new Uri(schema + filePathOfTheImage, UriKind.Absolute);
// Set the lock screen background image.
LockScreen.SetImageUri(uri);
// Get the URI of the lock screen background image.
var currentImage = LockScreen.GetImageUri();
System.Diagnostics.Debug.WriteLine("The new lock screen background image is set to {0}", currentImage.ToString());
}
}
private async Task DownloadImagefromServer(string imgUrl)
{
Debug.WriteLine("Attempting to Get Image from Server...");
WebClient client = new WebClient();
try
{
// I GET A "Cannot await Void" problem here?!?!?
//=================================================================
var result = await client.OpenReadAsync(new Uri(imgUrl, UriKind.Absolute));
BitmapImage bitmap = new BitmapImage();
bitmap.SetSource(result);
return result;
}
catch (Exception e)
{
Debug.WriteLine(e);
}
client.OpenReadCompleted += new OpenReadCompletedEventHandler(client_OpenReadCompleted);
//client.OpenReadAsync(new Uri(imgUrl, UriKind.Absolute));
}
//------------------------------------------------------------------
//THIS FUNCTION IS NEVER HIT WHEN RUN IN THE ScheduleAgent.cs CODE, HOWEVER
// WHEN IT IS RUN INT HE MainPage.xaml.cs CODE (Which is a copy/paste) IT RUNS FINE
// WHY IS IT NOT HITTING THIS? HOW CAN I FIX IT?
//------------------------------------------------------------------
void client_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
BitmapImage bitmap = new BitmapImage();
bitmap.SetSource(e.Result);
//img.Source = bitmap;
// Create a filename for JPEG file in isolated storage.
String tempJPEG = "DownloadedWalleper.jpg";
// Create virtual store and file stream. Check for duplicate tempJPEG files.
using (IsolatedStorageFile myIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication())
{
if (myIsolatedStorage.FileExists(tempJPEG))
{
myIsolatedStorage.DeleteFile(tempJPEG);
}
IsolatedStorageFileStream fileStream = myIsolatedStorage.CreateFile(tempJPEG);
StreamResourceInfo sri = null;
Uri uri = new Uri(tempJPEG, UriKind.Relative);
sri = Application.GetResourceStream(uri);
//BitmapImage bitmap = new BitmapImage();
//bitmap.SetSource(sri.Stream);
WriteableBitmap wb = new WriteableBitmap(bitmap);
// Encode WriteableBitmap object to a JPEG stream.
Extensions.SaveJpeg(wb, fileStream, wb.PixelWidth, wb.PixelHeight, 0, 85);
//wb.SaveJpeg(fileStream, wb.PixelWidth, wb.PixelHeight, 0, 85);
fileStream.Close();
}
LockScreenChange("DownloadedWalleper.jpg", false);
}
}
I would recommend using the "await" keyword. This will cause the code to pause and wait for the action to complete without needing to worry about mutexes or other thread-blocking strategies.
Unfortunately Windows Phone is missing the non-async methods on WebClient, so there's a little bit of setup, blatantly stolen from this answer.
To use async with WebClient, install the Microsoft.Bcl.Async NuGet package, and then you can use OpenReadTaskAsync
private async void DownloadImagefromServer(string imgUrl)
{
Debug.WriteLine("Attempting to Get Image from Server...");
WebClient client = new WebClient();
var result = await client.OpenReadTaskAsync(new Uri(imgUrl, UriKind.Absolute));
BitmapImage bitmap = new BitmapImage();
bitmap.SetSource(result);
//img.Source = bitmap;
... rest of your code from OpenReadCompleted goes here
}
So what happens is that execution waits for the "OpenRead" call to complete before it processes the result and then returns control.
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;