An image from byte to optimized web page presentation - asp.net

I get the data of the stored image on database as byte[] array;
then I convert it to System.Drawing.Image like the code shown below;
public System.Drawing.Image CreateImage(byte[] bytes)
{
System.IO.MemoryStream memoryStream = new System.IO.MemoryStream(bytes);
System.Drawing.Image image = System.Drawing.Image.FromStream(memoryStream);
return image;
}
(*) On the other hand I am planning to show a list of images on asp.net pages as the client scrolls downs the page. The more user gets down and down on the page he/she does see the more photos. So it means fast page loads and rich user experience. (you may see what I mean on www.mashable.com, just take care the new loads of the photos as you scroll down.)
Moreover, the returned imgae object from the method above, how can i show it in a loop dynamically using the (*) conditions above.
Regards
bk

Well, I think the main bottleneck is actually hitting the database each time you need an image. (Especially considering many users accessing the site.)
I would go with the following solution:
Database will store images with the original quality;
.ashx handler will cache images on the file system in various needed resolutions (like 32x32 pixels for icons, 48x48 for thumbnails, etc.) returning them on request and accessing database only once; (in this example is shown how to return an image via ashx handler)
The actual pages will point to .ashx page to get an image. (like <img scr="GetImage.ashx?ID=324453&Size=48" />)
UPDATE:
So the actual workflow in the handler will be like:
public void ProcessRequest (HttpContext context)
{
// Create path of cached file based on the context passed
int size = Int32.Parse(context.Request["Size"]);
// For ID Guids are possibly better
// but it can be anything, even parameter you need to pass
// to the web service in order to get those bytes
int id = Int32.Parse(context.Request["Id"]);
string imagePath = String.Format(#"images/cache/{0}/{1}.png", size, id);
// Check whether cache image exists and created less than an hour ago
// (create it if necessary)
if (!File.Exists(imagePath)
|| File.GetLastWriteTime(imagePath) < DateTime.Now.AddHours(-1))
{
// Get the file from the web service here
byte[] imageBytes = ...
// Save as a file
using (var memoryStream = new MemoryStream(imageBytes))
using (var outputStream = File.OpenWrite(imagePath))
Image.FromStream(memoryStream).Save(outputStream);
}
context.Response.ContentType = "image/png";
context.Response.WriteFile(imagePath);
}

Related

Is there a cross-platform solution to ImageSource to byte[]?

I did researches and fell on this solution: http://forums.xamarin.com/discussion/22682/is-there-a-way-to-turn-an-imagesource-into-a-byte-array
Initial question: http://forums.xamarin.com/discussion/29569/is-there-a-cross-platform-solution-to-imagesource-to-byte#latest
We want to upload an image through a HTTP Post, here's what we tried:
HttpClient httpClient = new HttpClient ();
byte[] TargetImageByte = **TargetImageSource**; //How to convert it to a byte[]?
HttpContent httpContent = new ByteArrayContent (TargetImageByte);
httpClient.PostAsync ("https://api.magikweb.ca/debug/file.php", httpContent);
We also are having a hard time with the libraries we gotta include in the using clauses. It seems like using System.IO; works, but it doesn't give us access to classes like FileInfo or FileStream.
Anybody has any idea how this can be done aside from custom platform-specific converters?
Possibly a Xamarin.Forms.ImageSource function toByte()?
Lemme know if you need more information.
TargetImageSource is a Xamarin.Forms.ImageSource.
ImageSource TargetImageSource = null;
Solution (Sten was right)
The ImageSource has to originate from another type to exist, that previous type can be converted to a byte[]. In this case, I use the Xamarin.Forms.Labs to take a picture and it returns a MediaFile in which a FileStream is accessible through the Source property.
//--Upload image
//Initialization
HttpClient httpClient = new HttpClient ();
MultipartFormDataContent formContent = new MultipartFormDataContent ();
//Convert the Stream into byte[]
byte[] TargetImageByte = ReadFully(mediaFile.Source);
HttpContent httpContent = new ByteArrayContent (TargetImageByte);
formContent.Add (httpContent, "image", "image.jpg");
//Send it!
await httpClient.PostAsync ("https://api.magikweb.ca/xxx.php", formContent);
App.RootPage.NavigateTo (new ClaimHistoryPage());
The function:
public static byte[] ReadFully(Stream input)
{
using (MemoryStream ms = new MemoryStream()){
input.CopyTo(ms);
return ms.ToArray();
}
}
I think you're looking at it a bit backwards.
ImageSource is a way to provide a source image for Xamarin.Forms.Image to show some content. If you're already showing something on the screen your Image view was populated with data that came from elsewhere, such as a file or resource or stored in an array in memory... or however else you got that in the first place. Instead of trying to get that data back from ImageSource you can keep a reference to it and upload it as needed.
Maybe you can elaborate a bit on your particular need if you don't feel this solution applies to your case.
Pseudo code:
ShowImage(){
ImageSource imageSource = ImageSource.FromFile("image.png"); // read an image file
xf_Image.Source = imageSource; // show it in your UI
}
UploadImage(){
byte[] data = File.ReadAll("image.png");
// rather than
// byte[] data = SomeMagicalMethod(xf_Image.Source);
HttpClient.Post(url, data);
}
UPDATE:
Since you're taking a picture you can copy the MediaFile.Source stream into a memory stream, then you can reset the memory stream's position to point at the beginning of the stream so that you can read it once again and copy it to the http body.
Alternatively you can store the MediaFile.Source to a file and use ImageSource.FromFile to load it in the UI, and when necessary - you can copy the file's contents into an http post body.

Image resizing for image gallery on Tridion 2011

I'm currently working on a web site that will show kind a image gallery on some detail pages. It must show a navigation at the bottom with small thumbnail images and it must show per each element some basic information and the big image.
The big image must be resized too, because there is a maximun size allowed for them.
The point is to use just a source image per multimedia component and being able to resize the images on publishing time so, from the source image would be sent to the client browser a thumbnail and a big image. It's possible to show small and big images using just styles or HTML, but this is quite uneficient because the source (some of them really heavy) image is always sent to the customer.
My first thought was a custom code fragment, something written in C# but I find complicated to resize only some images to a certain size and then resize them again to another size too. I don't find the way to replace the SRC on the final HTML with the appropiate paths neither.
Another idea was to create an old-style PublishBinary method but I find this really complex because looks like the current Tridion architecture is not meant to do this at all...
And the most important point, even in case we can do the resizing succesfully (somehow) it's currently a Tridion 2011 issue to publish twice the same image. Both the big and the small version would came actually from the same multimedia component so shouldn't be possible to publish both of them or playing with the names, the first one would be allways gone, because the path would be updated with the second one :-S.
Any ideas?
I have built an image re-sizing TBB in the past which reads the output of a Dreamweaver or XSLT template. The idea is to produce a tag like the following with the first template.
<img src="tcm:1-123" maxWidth="250" maxHeight="400"
cropPosition="middle" variantId="250x400"
action="PostProcess" enlargeIfTooSmall="true"
/>
The "Re-Sizing" TBB then post processes the Output item in the package, looking for nodes with the PostProcess action.
It then creates a variant of the Multimedia Component using the System.Drawing library according to the maxHieght and maxWidth dimention attributes, and publishes it using the AddBinary() method #frank mentioned and using the variantId attribute for a filename prefix, and variant id (and replaces the SRC attribute with the URL of the new binary).
To make this 100% flexible, if either of the maxHeight or maxWidth attributes are set to 0, the TBB re-sizes based on just the "non-zero" dimension, or if both are set it crops the image based on the cropPosition attribute. This enables us to make sqare thumbnails for both landscape and portrait images without distorting them. The enlargeIfTooSmall attribute is use to prevent small images from being stretched to much.
You can see samples of the final galleries here: http://medicine.yale.edu/web/help/examples/specific/photo/index.aspx
and other image re-sizeing examples here:
http://medicine.yale.edu/web/help/examples/general/images.aspx
All of the images are just uploaded to the CMS once, and then re-sized and cropped on the fly at publish time.
Tridion can perfectly well publish multiple variants on a single MMC. When you call AddBinary you can specify that this binary is a variant of the MMC, with each variant being identified by a simple string that you specify.
public Binary AddBinary(
Stream content,
string filename,
StructureGroup location,
string variantId,
Component relatedComponent,
string mimeType
)
As you can see you can also specify the filename for the binary. If you do, it is your responsibility that variants have unique filenames and filenames between different MMCs remain unique. So typically, it is easiest to simply prefix or suffix the filename with some indication of the variantId: <MmcImageFileName>_thumbnail.jpg.
For a recent demo project, I took a completely different approach. The binaries are all published to a broker database. They are extracted from the broker with an HttpModule, which writes the binary data to the file system.
I made it possible to encode the desired width or height in the URL of the image (of course, for binaries that are not images this part of the logic will not work). The module then resizes the image on the fly (truly on the fly, not during publishing!) and writes the resized version to the disk.
For example: if I request /Images/photo.jpg, I will get the original image. If I request /Images/photo_h100.jpg, I get a version of 100 pixels high. The url /Images/photo_w150.jpg leads to a width of 150 pixels.
No variants needed, no republishing because of different size requirements either: resizing is completely done on demand! The performance penalty on the server is negligible: each size is generated only once, until the image is republished.
I used .NET, but of course it can work in Java as well.
Following the Frank's and Quirijn's answer you may be interested on resize the image in a Cartridge Claims processor using the Ambient Data Framework. This solution would be technology agnostic and can be re-used in both Java and .Net. You just need to put the resized image bytes in a Claim and then use it in Java or .Net.
Java Claims Processor:
public void onRequestStart(ClaimStore claims) throws AmbientDataException {
int publicationId = getPublicationId();
int binaryId = getBinaryId();
BinaryContentDAO bcDAO = (BinaryContentDAO)StorageManagerFactory.getDAO(publicationId, StorageTypeMapping.BINARY_CONTENT);
BinaryContent binaryContent = bcDAO.findByPrimaryKey(publicationId, binaryId, null);
byte[] binaryBuff = binaryContent.getContent();
pixelRatio = getPixelRatio();
int resizeWidth = getResizeWidth();
BufferedImage original = ImageIO.read(new ByteArrayInputStream(binaryBuff));
if (original.getWidth() < MAX_IMAGE_WIDTH) {
float ratio = (resizeWidth * 1.0f) / (float)MAX_IMAGE_WIDTH;
float width = original.getWidth() * ratio;
float height = original.getHeight() * ratio;
BufferedImage resized = new BufferedImage(Math.round(width), Math.round(height), original.getType());
Graphics2D g = resized.createGraphics();
g.setComposite(AlphaComposite.Src);
g.drawImage(original, 0, 0, resized.getWidth(), resized.getHeight(), null);
g.dispose();
ByteArrayOutputStream output = new ByteArrayOutputStream();
BinaryMeta meta = new BinaryMetaFactory().getMeta(String.format("tcm:%s-%s", publicationId, binaryId));
String suffix = meta.getPath().substring(meta.getPath().lastIndexOf('.') + 1);
ImageIO.write(resized, suffix, output);
binaryBuff = output.toByteArray();
}
claims.put(new URI("taf:extensions:claim:resizedimage"), binaryBuff);
}
.Net HTTP Handler:
public void ProcessRequest(HttpContext context) {
if (context != null) {
HttpResponse httpResponse = HttpContext.Current.Response;
ClaimStore claims = AmbientDataContext.CurrentClaimStore;
if (claims != null) {
Codemesh.JuggerNET.byteArray javaArray = claims.Get<Codemesh.JuggerNET.byteArray>("taf:extensions:claim:resizedimage");
byte[] resizedImage = javaArray.ToNative(javaArray);
if (resizedImage != null && resizedImage.Length > 0) {
httpResponse.Expires = -1;
httpResponse.Flush();
httpResponse.BinaryWrite(resizedImage);
}
}
}
}
Java Filter:
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
ClaimStore claims = AmbientDataContext.getCurrentClaimStore();
if (claims != null) {
Object resizedImage = claims.get(new URI("taf:extensions:claim:resizedimage"));
if (resizedImage != null) {
byte[] binaryBuff = (byte[])resizedImage;
response.getOutputStream().write(binaryBuff);
}
}
}

RDLC Report Viewer Control - How to open images in another window/tab

I have images in the database (SQlServer 2008 R2) in a varbinary column. My report is developed in ASP.net 3.5 in Visual Studio 2010. On my report in a table, I use an Image, set the MIME type to image/jpeg, image source as Database & the image is displayed correctly in the report. Everything works!! Now my issue is that, I would like to add a hyperlink to that image so that when the user clicks on the image it opens in another window/tab in its original format. This will help the user to get a closer look at the image.
I tried to add Hyperlink - Go to URL - Expression
="http://localhost:49170/MtkMobileDeposit_FailedTransaction/Account/ReportByUserName.aspx?BackImageOriginal=" & Fields!BackImageOriginal.Value
But I cannot get it to display only the image. Instead it displays the entire report in a new tab. How do I first get the URL for the images to work ? Please help.
Thanks,
sdd
Your best bet is to use a httphandler to process your request.
Create an ImageHandler.ashx that will take in the record id. Then you can get the image's byte array and out put it to the screen.
Your new URL will look like
"/ImageHandler.ashx?Record_ID=" & Fields!RecordId.Value
public class ImageHandler : IHttpHandler
{
public int Record_ID
{
get { return Convert.ToInt32(HttpContext.Current.Request.QueryString["Record_ID"]); }
}
public void ProcessRequest(HttpContext context)
{
// get image from database into a byte array
Component.ImageController objImageController = new Component.ImageController();
Component.ImageInfo objImageInfo = objImageController.getImage(Record_ID);
byte[] byteArray = objImageInfo.photo;
// output it to the screen
context.Response.Clear();
context.Response.ContentType = "image/jpeg";
context.Response.BinaryWrite(byteArray);
context.ApplicationInstance.CompleteRequest();
}
public bool IsReusable
{
get
{
return false;
}
}
}

load large image from database and return it to the client side

Hi:
IN my application,I have some images saved in the db,so I create a ImgDownLoad.aspx to retrive the image and retun them,since the image in the db may very large(some of them is more than 20M),so I generate some thumbnails ,this is the code:
page_load(){
string id=Requset.QueryString["id"];
string imgtype=Requset.Querystring["itype"];
if(imgType=="small")
{
//request the thumbnail
string small_loaction=getSmallLocationById(id);
if(!File.exists(small_location)
{
byte[] img_stream =getStreamFromDb(id);
Image img=Image.frameStream(new MemsorStream(img_steam));//here,I often get the out of memory error,but I am not sure when it will happen.
generateSmallImage(img,location)
}
Response.TransferFile(small_location);
}
else if(imgType=="large"){
byte[] img_stream =getStreamFromDb(id);
new MemorySteam(img_stream).writeTo(Response.outputstream);
}
}
Anything wrong?
ALso,since I do not know the image format,so I can not add the
Response.contenttype="image/xxx";
What confusing me most is that I will meet the out of memory error,so I change the code:
try{
byte[] img_stream =getStreamFromDb(id);
Image img=Image.frameStream(new MemsorStream(img_steam));//here,I often get the out of memory error,but I am not sure when it will happen.
generateSmallImage(img,location)
}
catche(exceptin e){
//the small image can not generated,just return the whole image
new MemorySteam(img_stream).writeTo(Response.outputstream);
return;
}
In this case,I will avoid the out of memory problem,but some large image can not downloaded sometime.
So I wonder if there are any ways to handle the large image stream?
Take a large image for exmaple:
resolution:12590x4000
size:26M.
In fact,I have opened a large image(almost 24M) with the mspaint,and then save the image again,I found that it size is much smaller than at first. So is it possible to resize the image in the server side? Or other good manners to hanle my problem?
Firstly, you're not disposing of the Image and Stream type instances that you create - given subsequent calls, over time, this is bound to cause issues; particularly with images around the 20meg mark!
Also, why create the thumbnails every call? Create once and cache, or flush to disk: either way, serve a 'one you made earlier' rather than do this processing over and over.
I would recommend, however, you try an minimise the size (in bytes) of the images. Some might argue that they shouldn't be in the database if over 1meg, but store them on disk and a file name in the database. I guess that's open for debate, browse if interested.
To your comment, I'd urge you not to allow other scopes take control of resources 'owned' by another; dispose of items in the scope that creates them (obviously sometimes some things need to stick around, but what is responsible for them should be clear). Here's a little rework of some of your code:
if (imgType == "small")
{
string small_loaction = getSmallLocationById(id);
if(!File.exists(small_location)
{
byte[] imageBytes = getStreamFromDb(id);
using (var imageStream = new MemoryStream(imageBytes))
{
using (var image = Image.FromStream(imageStream))
{
generateSmallImage(image, small_location)
}
}
}
Response.TransferFile(small_location);
}
else if (imgType=="large")
{
byte[] imageBytes = getStreamFromDb(id);
Response.OutputStream.Write(imageBytes, 0, imageBytes.Length);
}

Caching http handler .ashx output

Im creating an image which has some text in it, for every customer, the image contains their name and I use the Graphics.DrawString function to create this on the fly, however I should not need to create this image more than once, simply because the name of the customer should hardly change, but I do not want to store it on disk.
Now I am creating the image in a handler i.e :
<asp:Image ID="Image1" runat="server" ImageUrl="~/imagehandler.ashx?contactid=1" />
What is the best way to cache the image that comes back? Should I cache the bitmap it creates? Or cache the stream that I pass back? And which cache object should I use, I gather there are many different ways? But output caching doesn't work on http handlers right? What is the recommended way? (I'm not bothered about caching on client side, I'm on about server side) Thanks!
The simplest solution I can think of would be to just cache the Bitmap object in the HttpContext.Cache after you've created it in the image handler.
private Bitmap GetContactImage(int contactId, HttpContext context)
{
string cacheKey = "ContactImage#" + contactId;
Bitmap bmp = context.Cache[cacheKey];
if (bmp == null)
{
// generate your bmp
context.Cache[cacheKey] = bmp;
}
return bmp;
}
David,
you can use output caching on handler. not declaratively but in code behind.
see if you can use following snippet.
TimeSpan refresh = new TimeSpan(0, 0, 15);
HttpContext.Current.Response.Cache.SetExpires(DateTime.Now.Add(refresh));
HttpContext.Current.Response.Cache.SetMaxAge(refresh);
HttpContext.Current.Response.Cache.SetCacheability(HttpCacheability.Server);
HttpContext.Current.Response.Cache.SetValidUntilExpires(true);
//try out with – a simple handler which returns the current time
HttpContext.Current.Response.ContentType = "text/plain";
HttpContext.Current.Response.Write("Hello World " + DateTime.Now.ToString("HH:mm:ss"));

Resources