In xamarin forms we can create images like this:
Image i = new Image { Source = "http://www.foo.com/foo.jpg };
After adding this to layout if url returns an image it will display it. What I want to now is is there a way to know if ths Url is an actual image. Otherwise I am going to show an default image.
Regards.
Edit
I have created a function:
public string GetImageSourceOrDefault(string orgUrl)
{
var req = (HttpWebRequest)WebRequest.Create(orgUrl);
req.Method = "HEAD";
try
{
using (var resp = req.GetResponse())
{
bool res = resp.ContentType.ToLower(CultureInfo.InvariantCulture)
.StartsWith("image/");
if (res)
return orgUrl;
else
return "defualt_logo.jpg";
}
}
catch
{
return "default_logo.jpg";
}
}
This function does the trick. However, for every image it does a request. I have a listview which shows like 220 entries. Using this method messed up the time that listview gets loaded.
Note: this function is natively called using dependency injection.
Maybe further improvements will do. Any ideas?
FFImageLoading CachedImage supports Loading and Error Placeholders (and much more). It's basically a API compatible replacement for Image with additional properties. You could try that.
var cachedImage = new CachedImage() {
LoadingPlaceholder = "Loading.png",
ErrorPlaceholder = "Error.png"
};
https://github.com/molinch/FFImageLoading
With Xamarin.Forms UriImageSource you can specify different caching length, and whether caching is used by using the properties CacheValidity and CachingEnabled.
By default it will automatically cache results for 1 day on the local storage of the device.
In your function, as you mention, you are downloading the image every single time.
You have no current functionality that is storing and caching the result for later re-use.
By implementing something like this on the platform specific layer would get around your current solution of re-downloading the image every single time.
Alternatively as a workaround, if you didn't want to implement the above, you could try putting two Image controls stacked upon each other, maybe in a Grid, with the bottom image showing a default placeholder image, and on-top another Image control that would show the intended image, if successfully downloaded, using the UriImageSource.
You could also possibly hook hook into the PropertyChange notification of the Image.Source and detect it being set, with the image then being displayed. Upon detection you could then release the image from the temporary place holder Image control perhaps?
Related
I'm creating a mockup file upload tool for a community site using Fine Uploader.
I've got the session set up to retrieve the initial files from the server along with a thumbnail url.
It all works great, however the rendering of the thumbnails is really slow.
I can't work out why. So I hard-coded to use a very small thumbnail for each of the four files. This made no difference.
The server side not the issue. The information is coming back very quickly.
Am I doing something wrong? Why is fineuploader so slow? Here's screen grab. It's taking four seconds to render the four thumbnails.
I'm using latest chrome. It's a NancyFX project on a fairly powerful machine. Rending other pages with big images on them is snappy.
Client side code:
thumbnails: {
placeholders: {
waitingPath: '/Content/js/fine-uploader/placeholders/waiting-generic.png',
notAvailablePath: '/Content/js/fine-uploader/placeholders/not_available-generic.png'
}
},
session: {
endpoint: "/getfiles/FlickaId/342"
},
Server side code:
// Fine uploader makes session request to get existing files
Get["/getfiles/FlickaId/{FlickaId}"] = parameters =>
{
//get the image files from the server
var i = FilesDatabase.GetFlickaImagesById(parameters.FlickaId);
// list to hold the files
var list = new List<UploadedFiles>();
// build the response data object list
foreach (var imageFile in i)
{
var f = new UploadedFiles();
f.name = "test-thumb-small.jpg"; // imageFile.ImageFileName;
f.size = 1;
f.uuid = imageFile.FileGuid;
f.thumbnailUrl = "/Content/images/flickabase/thumbnails/" + "test-thumb-small.jpg"; // imageFile.ImageFileName;
list.Add(f);
}
return Response.AsJson(list); // our model is serialised by Nancy as Json!
};
This is by design, and was implemented both to prevent the UI thread from being flooded with the image scaling logic and to prevent a memory leak issue specific to Chrome. This is explained in the thumbnails and previews section of the documentation, specifically in the "performance considerations" area:
For browsers that support client-generated image previews (qq.supportedFeatures.imagePreviews === true), a configurable pause between template-generated previews is in effect. This is to prevent the complex process of generating previews from overwhelming the client machine's CPU for a lengthy amount of time. Without this limit in place, the browser's UI thread runs the risk of blocking, preventing any user interaction (scrolling, etc) until all previews have been generated.
You can adjust or remove this pause via the thumbnails option, but I suggest you not do this unless you are sure users will not drop a large number of complex image files.
I'm trying to get all the photos from the device's pictures library and show then on the app using a <GridView/> with an <Image/> inside it's item template, but I didn't find a way to do that without issues.
I need to create BitmapImages from the StorageFiles that I get.
First I tried creating the BitmapImages and setting the UriSource as new Uris with the files paths, like this:
var picsLib = await KnownFolders.PicturesLibrary.GetFilesAsync(CommonFileQuery.OrderByDate);
var picsList = new List<BitmapImage>();
foreach (StorageFile pic in picsLib)
{
var imgSrc = new BitmapImage();
imgSrc.UriSource = new Uri(pic.Path, UriKind.Absolute);
picsList.Add(imgSrc);
}
PhotosView.ItemsSource = picsList;
But the images doesn't show up.
Right after, I tried using streams:
var imgSrc = new BitmapImage();
var picStream = await pic.OpenReadAsync();
imgSrc.SetSource(picStream);
picsList.Add(imgSrc);
Of course, I got System.OutOfMemoryException.
Next, I tried using thumbnails:
var imgSrc = new BitmapImage();
var picThumb = await pic.GetThumbnailAsync(Windows.Storage.FileProperties.ThumbnailMode.PicturesView,
200, Windows.Storage.FileProperties.ThumbnailOptions.ResizeThumbnail);
imgSrc.SetSource(picThumb);
picsList.Add(imgSrc);
But I realized that it's just like the stream, OutOfMemory again. If I limit it to only get the thumbnails of 10 or 20 images, it works nice, but I really need to show all the photos.
XAML isn't the problem as it does the job fine when I limit the number of images to load.
The app is meant to be used by anyone who download it from the Windows Phone Store when finished, so the size of the images vary, as the pictures library of Windows Phone devices contains almost any photos stored on the user's phone, including photos from the device's camera, saved images, etc.
There is absolutely no way to ever guarantee that you won't run out of memory with any of the above approaches. And the reality is that unless you are resizing the images on the fly to a standard size, you will never really control how much memory you are using, even for just the visible images.
You MUST make the containing grid virtualized so that the byte array's are only allocated for the images that are actually visible to the user.
Yes, there will be some lag on most systems as you scroll as byte arrays are discarded and created, but that is the price you pay for being able to view them 'all'.
All of that being said, here is a blog to help get you started.
We have made a custom image cropper.
The idea is that it saves a query string in a field in the item.
Then on rendering we add that query string to the image url.
We added a getMediaStream pipeline. Inside this pipeline we crop the image using ImageProcessor based on the querystring that the image has.
This all works... once.
After it worked once the image is in the media cache and we never get back inside the getMediaStream pipeline. Because when you recrop, you don't change the image but you change that querystring.
We are searching for a way that when we save a new crop, we clear the media cache from that image.
We have tried the following:
var mediaItem = new MediaItem(imageField.MediaItem);
var media = MediaManager.GetMedia(mediaItem);
var mediaOptions = new MediaOptions();
var mediaStream = MediaManager.Cache.GetStream(media, mediaOptions);
MediaStream cachedStream = null;
MediaManager.Cache.AddStream(media, mediaOptions, mediaStream, out cachedStream);
We get the media cache stream and try setting it to zero, but that gives us the following exception: Could not create the buffer file needed for stream sharing
Any ideas if it's possible to clear a single item from the media cache?
I don't think you need to remove a single item from the cache. Sitecore stores one image in the cache per unique request. If you have genuinely updated the querystring that is used to request the image then Sitecore should call the getmediastream pipeline again.
My guess is that your custom querystring is not actually being passed as part of the request.
Sitecore stores a ini file alongside the cached images, It has a section per image request and stores the querystring that was requested. Bottom line, if your querystring is unique then you should hit the pipeline.
I'm so stuck on something i thought would be easy.
I have a DLL that returns an Image object.
I just cant figure out how to display that image on a webpage.
I've tried a few ways, and google a million different variations.
Is it not possible to just bind an Image object to an element on the page like an HtmlImage or a simple img?
Or do i need to convert the Image to a Stream? or a Bitmap? I'm really stuck!
Any help appreciated.....
V
With Asp.Net WebForm, the easiest way is to create a custom ashx file.
In Visual Studio, create a new Custom Handler (I'm not sure of the name of the template in Visual Studio). This will create a .ashx file.
In the code of this handler, write something like (does not have VS under the hand to test the syntax) :
public void ProcessRequest(System.Web.HttpContext context)
{
byte[] raw;
using(var ms = new MemoryStream()){
Image myImage = GetFromDll();
myImage.Save(ms, ImageFormat.Png);
raw=ms.ToArray();
}
context.Response.ContentType = "image/png";
context.Response.BinaryWrite(raw);
}
Then, in your browser, navigate to http://yourserver/app/yourhandler.ashx.
You can if you want add url parameter, and get it from the Request.QueryString collection
It's not as simple as binding. On the client side images are retrieved from the web server as a separate GET request, which means you have to have a URL that resolves to an image. The other option, as Asif suggested, is embedding your image in the HTML as a Base64 string, which is bad practice for shared images (see Steve B's comment).
You either have to provide an URL (route that returns the image file in MVC, or a custom page with proper content type and Response.Write in WebForms), or embed in html.
EDIT:
There is also a third option involving custom HTTP handlers. These have the advantage of bypassing the app framework and serving the content almost directly off the web server, see MSDN.
Convert your image to base64 string and then set it in the <img/> tag.
<img/> can show the image in base64 string.
Alternatively you can save the image and use the path in the <img/>.
I'm creating a regular gallery in ASP.NET but I have little experiences with creation of thumbnails. I know the algorithms and the GetThumbnailImage method, but my problem is somewhere else - I'm currently displaying the images (just resized) using the ImageButton control. And that's the point - I have no idea how to hook up the "thumbnailed" image to the ImageUrl property. Is it even possible and if yes, how? Or should I use some other control instead? Thanks for any suggestions!
It sounds like you need to set up an HttpHandler, which would create the resized image and probably cache it to disk as well, to save having to recreate the thumbnail on each request.
So, for example:
<asp:ImageButton ID="ImageButton1" ImageUrl="~/ImageHandler.ashx?ImageId=123" runat="server />
You would then have a handler:
namespace MyProject
{
public class ImageHandler : IHttpHandler
{
public virtual void ProcessRequest(HttpContext context)
{
// 1. Get querystring parameter
// 2. Check if resized image is in cache
// 3. If not, create it and cache to disk
// 5. Send the image
// Example Below
// -------------
// Get ID from querystring
string id = context.Request.QueryString.Get("ImageId");
// Construct path to cached thumbnail file
string path = context.Server.MapPath("~/ImageCache/" + id + ".jpg");
// Create the file if it doesn't exist already
if (!File.Exists(path))
CreateThumbnailImage(id);
// Set content-type, content-length, etc headers
// Send the file
Response.TransmitFile(path);
}
public virtual bool IsReusable
{
get { return true; }
}
}
}
You'd also need to set this up in web.config
<system.web>
<httpHandlers>
<add verb="*" path="ImageHandler.ashx" type="MyProject.ImageHandler, MyProject"/>
</httpHandlers>
</system.web>
This should be enough to get you started. You'll need to modify the ProcessRequest method to create the thumbnail, but you mentioned having taken care of this already. You'll also need to make sure that you set the headers correctly when transmitting the file to the browser.
You can create a HttpHandler which handles image requests and returns thumbnails (or does whatever you need on the images).
Whenever you do graphics stuff in ASP.NET, keep in mind that almost all of System.Drawing is a wrapper for GDI+ and thetrefore holds references to unmanaged memory which needs to be disposed properly (use the using statement). This holds true even for simple classes like StringFormat etc.
Http Handler is the way to go.
Another note on performance: manipulating images is expensive relative to disk space, both from a memory and cpu standpoint. Therefore generating the thumbnail from a full image is is something you only want to do once for each full image. The best time to do it is probably at the time where the image is uploaded, especially if you will be showing a number of these on the same page.