IHttpHandler for images producing a stackoverflow in IE - asp.net

I have a directory of images that reside outside the context of my web application that I need to serve to the user. Currently I'm using an IHttpHandler to serve the images and using some javascript to navigate through a set of images (the navigation is primitive for now). I followed examples for using IHttpHandler to serve images closely but when I view the images in firefox the browser hangs and when I view in IE I get a "Stack overflow at line: 0".
Code for the IHttpHandler
Public Class ShowImage : Implements IHttpHandler
Public Sub ProcessRequest(ByVal context As HttpContext) _
Implements IHttpHandler.ProcessRequest
Dim picid As String
If context.Request.QueryString("id") IsNot Nothing Then
picid = context.Request.QueryString("id")
Else
Throw New ArgumentException("No parameter specified")
End If
'' Convert Byte[] to Bitmap
context.Response.Cache.SetCacheability(HttpCacheability.NoCache)
context.Response.Cache.SetNoStore()
context.Response.Cache.SetExpires(DateTime.MinValue)
Dim newBmp As Bitmap = GetPhoto(picid)
If newBmp IsNot Nothing Then
Dim imgGraphics As Graphics = Graphics.FromImage(newBmp)
imgGraphics.DrawImageUnscaled(newBmp, 0, 0, 640, 480)
context.Response.StatusCode = 200
context.Response.ContentType = "image/jpeg"
newBmp.Save(context.Response.OutputStream, ImageFormat.Jpeg)
newBmp.Dispose()
Else
'' Return 404
context.Response.StatusCode = 404
context.Response.End()
End If
End Sub
...
Public ReadOnly Property IsReusable() As Boolean _
Implements IHttpHandler.IsReusable
Get
Return True
End Get
End Property
End Class
Here is the javascript code that's calling the IHttpHandler defined above:
function updateImage(){
var ddlPhotos = document.getElementById("ddlPhotos");
var selected = ddlPhotos.options[ddlPhotos.selectedIndex].value;
if( selected != -1 ){
// Update the image
retrievePicture(document.getElementById("propertyImage"), selected)
}
}
function retrievePicture(imgCtrl, picid)
{
imgCtrl.src = 'ShowImage.ashx?id=' + picid;
}
Finally here's the img tag that is the "place holder":
<img src="#"
alt="Property Photo"
width="640px"
height="480px"
id="propertyImage"
onload="retrievePicture(this, '<%= pictureId.value %>');"
/>
I'm confused as to why the javascript seems to spiral out of control...

My guess - not being a JavaScript expert - is that the onload event is triggered any time the image finishes loading. In other words, as soon as the image is loaded, it triggers loading a new one... which triggers loading a new one... which triggers loading a new one etc.
You will probably be able to see that in multiple calls to the server for the same image - unless the browser is caching it, of course. Anyway, you'll either need to trigger it in some other way, or make the trigger detect that the image which has been loaded is already the right one, and there's no need to replace it.

I suspect the act of changing the src and loading a new image may be triggering the "onload" event of the image again.
Try clearing the event before setting the source, will probably look similar to this:
function retrievePicture(imgCtrl, picid)
{
imgCtrl.onload = null;
imgCtrl.src = 'ShowImage.ashx?id=' + picid;
}

Related

Getting a ScriptReference from a ScriptResourceMapping definition in a custom control

I am building a custom control with client side scripts that I would like to reference using ScriptManager.ScriptResourceMapping (to make use of the Path and DebugPath attributes).
I would like the custom control to be easily ported to other projects - i.e. I would like to drag and drop the codebehind files (and eventually make the control a separate DLL, but for now the drag and drop will suffice). I would therefore like to avoid (1) having the client script as an embedded resource, (2) referenced as a WebResource in the AssemblyInfo, or (3) have the ScriptManager.ScriptResourceMapping.AddDefinition in global.asax.
In simple terms can I get the script management code to be in just the custom control's code?
At the moment I am getting an error stating that the script reference cannot be found in the assembly, and I guess I am setting the wrong assembly.
My custom control code is as follows:
Public Class MyControl
Inherits System.Web.UI.LiteralControl
Implements ISectionControl, IScriptControl
Private _scriptReference As ScriptReference
Public Sub New()
' Add the resource mapping
ScriptManager.ScriptResourceMapping.AddDefinition("MyControlScript", New ScriptResourceDefinition With {
.ResourceAssembly = System.Reflection.Assembly.GetExecutingAssembly,
.ResourceName = "MyControlScript.js",
.Path = "Path.To.MyControlScript.minimised.js",
.DebugPath = "Path.To.MyControlScript.original.js"
})
' Set the script reference
_scriptReference = New ScriptReference("MyControlScript.js", Assembly.GetExecutingAssembly.FullName)
End Sub
Protected Overrides Sub OnPreRender(e As System.EventArgs)
MyBase.OnPreRender(e)
' Register the script
ScriptManager.GetCurrent(Page).RegisterScriptControl(Of MyControl)(Me)
' Some code to set the Text of the literal control
' ...
End Sub
Public Function GetScriptDescriptors() As System.Collections.Generic.IEnumerable(Of System.Web.UI.ScriptDescriptor) Implements System.Web.UI.IScriptControl.GetScriptDescriptors
Return New ScriptDescriptor() {}
End Function
Public Function GetScriptReferences() As System.Collections.Generic.IEnumerable(Of System.Web.UI.ScriptReference) Implements System.Web.UI.IScriptControl.GetScriptReferences
Return New ScriptReference() {_scriptReference}
End Function
End Class
I hope the question makes sense. Thanks for taking the time to read through.
Ali
Answered this myself, I was getting confused with the assemblies and the constructors for ScriptReference. I just wanted a ScriptReference with the (mapped) name so I used the blank constructor and then set Name. I could then remove the assembly information.
Adjusting the following sorted the problem out:
Public Sub New()
' Add the resource mapping
ScriptManager.ScriptResourceMapping.AddDefinition("MyControlScript", New ScriptResourceDefinition With {
.Path = "Path.To.MyControlScript.minimised.js",
.DebugPath = "Path.To.MyControlScript.original.js"
})
' Set the script reference
_scriptReference = New ScriptReference() With {.Name="MyControlScript"}
End Sub

Add watermarktext to image dynamically without saving image to server

I have an image stored on my server in this folder:
\images\freemedia\largethumbs\test.png
On my default.aspx page I have an imagecontrol:
<asp:Image ID="Image1" runat="server" />
When the visitor requests the default.aspx page I want to get the test.png image from the server, add the watermark text "hello world" to it on the right bottom.
I DON'T want to save the watermarked image to the server, since I want to save storage and still want to have access to the original image.
From the image that is shown to the visitor, he should preferably not be able to derive the original filename, so he should not be allowed to see that the original filename is test.png.
I've been searching on Google a lot, but all examples save the watermarked image to disk, which I don't want.
I already have a httphandler:
Public Class pichandler : Implements IHttpHandler
Public Sub ProcessRequest(ByVal context As HttpContext) Implements IHttpHandler.ProcessRequest
Dim data As Byte()
Dim fName As String
Using w As New generaltablesTableAdapters.freemediaTableAdapter
fName = w.GetDataById(i)(0).medialink.ToString
End Using
data = My.Computer.FileSystem.ReadAllBytes(context.Server.MapPath("~/images/freemedia/thumbs/" & fName))
' --> how can I add a watermark text to the image here?!?!?!?
context.Response.ContentType = "image/jpeg"
context.Response.BinaryWrite(data)
End Sub
End Class
Does anyone have a code sample on how to do this?
If there's another way of doing this, that is fine too. But please show how I can serve the watermarked image as part of the final HTML.
Since you are working with asp.net, you might as well use an HttpHandler.
Here's an example, create a new generic handler, let's call it ImageHandler and the code can be as easy as this:
public class ImageHandler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
// Clear response and set content type to image.
context.Response.Clear();
context.Response.ContentType = "image/jpeg";
// Create your image, however you want it. Server.MapPath and so on.
var image = Bitmap.FromFile(#"C:\Images\image.jpg");
// And save it to the OutputStream.
image.Save(context.Response.OutputStream, ImageFormat.Jpeg);
}
public bool IsReusable
{
get { return false; }
}
}
And then just use it like this:
<asp:Image ID="Image1" ImageUrl="~/ImageHandler.ashx" runat="server" />
Of course you could also send in some QueryString parameters like this:
<asp:Image ID="Image1" ImageUrl="~/ImageHandler.ashx?filename=myimage.jpg" runat="server" />
And then just use context.Request.QueryString["filename"] in the ImageHandler.
UPDATE:
After the comments, here's how you can add a watermark:
Public Sub ProcessRequest(ByVal context As HttpContext) Implements IHttpHandler.ProcessRequest
Dim watermarkText As String = "Copyright"
Dim fName As String
Using w As New TopTrouwen.generaltablesTableAdapters.freemediaTableAdapter
fName = w.GetDataById(i)(0).medialink.ToString
End Using
' Create image directly from the path
Dim image = Drawing.Image.FromFile(context.Server.MapPath("~/images/freemedia/thumbs/" & fName))
Dim font As New Font("Tahoma", 16, FontStyle.Regular, GraphicsUnit.Pixel)
'Adds a transparent watermark
Dim color As Color = Drawing.Color.FromArgb(50, 0, 0, 0)
Dim brush As New SolidBrush(color)
Dim gr As Graphics = Graphics.FromImage(image)
' Measure the watermark text so we can offset it's width and height
Dim watermarkSize = gr.MeasureString(watermarkText, font)
' Create the point where the watermark text should be printed
Dim point As New Point(image.Width - watermarkSize.Width, image.Height - watermarkSize.Height)
gr.DrawString(watermarkText, font, brush, point)
gr.Dispose()
context.Response.ContentType = "image/jpeg"
image.Save(context.Response.OutputStream, ImageFormat.Jpeg)
End Sub

Large File Upload Using HttpHandler or HttpModule?

I have a webform application. It required to be able to upload large file (100MB). I intended to use httpHandler and httpModule to split the file to chunk.
I also had a look at http://forums.asp.net/t/55127.aspx
But it is a very old post and I've seen some example on the internet using httpHandler.
e.g. http://silverlightfileupld.codeplex.com/
I'm not sure httpModule is still better then httpHandler.
Since httpModule apples to the request of the whole application, and I just want it apply to specify page.
Can anybody explain the shortcoming of httpHandler for large file upload clearly (if it has)?
If you know a good example without flash/silverlight , could you post the link here? thx
Edit: Would Like to see some Source Code example.
Why not try plupload which has lot of features with many fallbacks and here how it is done.
This is the http handler code:
Imports System
Imports System.IO
Imports System.Web
Public Class upload : Implements IHttpHandler
Public Sub ProcessRequest(ByVal context As HttpContext) Implements IHttpHandler.ProcessRequest
Dim chunk As Integer = If(context.Request("chunk") IsNot Nothing, Integer.Parse(context.Request("chunk")), 0)
Dim fileName As String = If(context.Request("name") IsNot Nothing, context.Request("name"), String.Empty)
Dim fileUpload As HttpPostedFile = context.Request.Files(0)
Dim uploadPath = context.Server.MapPath("~/uploads")
Using fs = New FileStream(Path.Combine(uploadPath, fileName), If(chunk = 0, FileMode.Create, FileMode.Append))
Dim buffer = New Byte(fileUpload.InputStream.Length - 1) {}
fileUpload.InputStream.Read(buffer, 0, buffer.Length)
fs.Write(buffer, 0, buffer.Length)
End Using
context.Response.ContentType = "text/plain"
context.Response.Write("Success")
End Sub
Public ReadOnly Property IsReusable() As Boolean Implements IHttpHandler.IsReusable
Get
Return False
End Get
End Property
End Class

Server.Transfer losing local variables

For my application, I have a page that redirects to another page (within the same application) via Server.Transfer. I need to do this because the original page has an object that I need to access by using the Page.PreviousPage property.
Once my "destination" page has been fully loaded, a local deep clone that I made of the source page's object is suddenly released from memory once I perform a postback? Is this by design--something to do with the Server.Transfer?
An example...
Page1.aspx:
Public Structure myCustomObject
Implements ICloneable
Dim someField as String = "default value" ' Default value
Public Function Clone() As Object Implements System.ICloneable.Clone
Dim temp as new myCustomObject
temp.someField = Me.someField
Return temp
End Function
End Structure
Dim obj As myCustomObject
Public ReadOnly Property objProp as myCustomObject
Get
Return obj
End Get
End Property
objProp.someField = "changed value from source page"
Server.Transfer("page2.aspx", True)
Page2.aspx:
(onLoad)
Dim newObj As myCustomObject
newObj = Page.PreviousPage.objProp.Clone()
Debug.Write(newObj.someField) ' Output: "changed value from source page"
At this point, EVERYTHING works as it should. Stuff got cloned over correctly and all is well.
(Let's say this is on a button click event)
Debug.Write(newObj.someField) ' Output: "default value"<- This is NOT "changed value from source page" for some reason when it was working literally a few lines ago!
It's around here that I get the problem. My guess is that the Server.Transfer stops any association with the source page after the new page loads.
Is there a better way for cross-page object passing?
Just pass a variable in the HttpContext, you will have to handle your casting, not sure what Page.PreviousPage is:
Current Page:
HttpContext CurrContext = HttpContext.Current;
CurrContext.Items.Add("PreviousPage", Page.PreviousPage);
Transfered to page:
HttpContext CurrContext = HttpContext.Current;
var previousPage = CurrContext.Items["PreviousPage"];
Sorry for the C#, there was no code and the question wasn't marked with VB.NET when I answered. Someone feel free to convert.
If anyone is still looking for the solution to this issue, it's more than likely the settings.AutoRedirectMode = RedirectMode.Permanent; setting in your RouteConfig.cs. Change it to settings.AutoRedirectMode = RedirectMode.Off;
public static void RegisterRoutes(RouteCollection routes)
{
var settings = new FriendlyUrlSettings();
// settings.AutoRedirectMode = RedirectMode.Permanent;
settings.AutoRedirectMode = RedirectMode.Off;
routes.EnableFriendlyUrls(settings);
}

ASP.NET Is it possible to handle all the null session checking in masterpage

I have the following steps in my webpage
1) User Logs in and I set the following session variables
Session("userName") = reader_login("useremail").ToString()
Session("userId") = reader_login("user_ID").ToString()
Session("firstName") = reader_login("firstName").ToString()
2) Now on my logged in VB.NET templates I reference a MasterPage called LoggedIn.Master. In Which I added the following method to check for the above null session variables. And if they null to redirect back to login page.
Protected Sub Page_Init(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Init
'#Check that User is Logged in, if not redirect to login page
If (Session("userId") Is Nothing) Or (Session("userName") Is Nothing) Or (Session("firstName") Is Nothing) Then
Response.Redirect(ConfigurationManager.AppSettings("site_base_url").ToString & "login/", False)
End If
3) Now my question is if I want to use any above 3 Session variables in different .net templates or usercontrols referencing the above master page do i need to AGAIN add the check
If (Session("userId") Is Nothing) Or (Session("userName") Is Nothing) Or (Session("firstName") Is Nothing) Then
Response.Redirect(ConfigurationManager.AppSettings("site_base_url").ToString & "login/", False)
End If
In the respective pages or will the check in master page do. Because at the moment i.e. if in a usercontrol I attempt to do i.e.
customerName.Text = Session("userName").ToString()
or
Response.Write(Session("userName").ToString())
I am getting the error
Object reference not set to an instance of an object.
customerName.Text = Session("userName").ToString()
You can write a wrapper around the Session to handle null values and just call the wrapper when you access the items:
Public Class SessionWrapper
Public Shared ReadOnly Property Item()
'Access session here and check for nothing
End Property
End Class
And use it like this
SessionWrapper.Item("itemName")
In answer to your question - as long as the masterpage checks the session and redirects before all your controls and page code make a reference to Session, you should be fine.
You were using OnInit() which seems reasonable, but see this article for a good understanding of the timing of events.
Incidentally, I strongly discourage the use of ad-hoc calls to Session in your page and control code. Instead, I recommend you create a static SessionManager class that does the Session referencing for you. That way, you get to benefit from strong typing, and won't be able to accidentally make hard-to-debug 'session key' typos in your code like Session["FiirstName"]. Also, you can incorporate your null-session check right into the call for the session value:
EXAMPLE (in C#, sorry!)
public static class SessionManager
{
private static void EnsureUserId()
{
if (Session["userId"] == null)
{
Response.Redirect("YourLogin.aspx", false);
}
}
public static string FirstName
{
get
{
EnsureUserId();
if (Session["firstName"] == null)
Session["firstName"] = "";
return (string)Session["firstName"];
}
set
{
Session["firstName"] = value;
}
}
}
You can create an http module that asks about the session objects and if they are null, it will redirect to the login page and by developing this http module, in each page request the module will do the check and then you can use it normally without checking.
A better way to handle this would be to add a base class for all controls that require this session variables to be present. You can then add properties to wrap access to the session and other cool stuff and the check will work even if the controls are used with a different master page.

Resources