|
There are many ways of implementing this, but I’ve implemented MediaRSS for Emmas Bears by simply adding an ASPX page
to the main folder of the application using Visual Studio.
Rss.aspx contains the following code:
<%@ Page Language="VB" AutoEventWireup="false" CodeFile="rss.aspx.vb" Inherits="rss" %>
<%@ OutputCache Duration="900" VaryByParam="none" %>
<% RenderXML() %>
|
The OutputCache tag tells the server to cache the feed contents for 900 seconds (15 minutes). If anyone requests to view the
feed during a 15-minute period, they’ll always see the same feed. The server won’t render a new feed, it will just show whatever
was available 15 minutes ago.
This is important. If your feed is popular, thousands of people might start following it. They might tell their feed readers
to check for new content every 5 minutes. If a thousand people do this, your server will be hit with a thousand hits per minute.
If you implement OutputCache, your server can take a vacation for 15 minutes and serve up the same thing 5,000 times.
The RenderXML function call does all the work and spits out the XML code of the feed. Here’s part of what’s in the code-behind, rss.aspx.
Visual Basic:
Imports System
Imports System.Configuration
Imports System.Data
Imports System.Data.SqlClient
Imports System.Text
Imports System.Web
Imports System.Xml
Partial Class rss
'Inherits System.Web.UI.Page
Inherits BaseApplicationPage
Protected Sub RenderXML()
Dim X As New X1
' start with a clean response stream
Response.Clear()
' set the type to xml
Response.ContentType = "text/xml"
' create a new XML document for the response
Dim objX As New XmlTextWriter(Response.OutputStream, Encoding.UTF8)
objX.WriteStartDocument()
' create header info for the xml doc
objX.WriteStartElement("rss")
objX.WriteAttributeString("version", "2.0")
objX.WriteAttributeString("xmlns:media", "http://search.yahoo.com/mrss/")
objX.WriteStartElement("channel")
objX.WriteElementString("title", "EmmasBears.com Picture Feed")
objX.WriteElementString("link", "http://EmmasBears.com/rss.aspx")
objX.WriteElementString("description", _
"Quality hand crafted Teddy Bears from EmmasBears.com")
objX.WriteElementString("copyright", "(c) 2009, Emma Braiden. " & _
"All rights reserved.")
objX.WriteElementString("ttl", "15")
objX.WriteRaw(vbNewLine)
' Do we want to just show a single picture?
Dim ShowSingle As Boolean = False
If Not IsNothing(Request.QueryString("single")) Then
ShowSingle = True
End If
' get latest 10 rows in the table
Dim ob As New BaseClasses.Data.OrderBy(False, False)
ob.AddColumn(ItemsTable.LastUpdated, BaseClasses.Data.OrderByItem.OrderDir.Desc)
Dim irecs() As ItemsRecord = ItemsTable.GetRecords("1=1", ob, 0, 10)
' spin through each item
For Each irec As ItemsRecord In irecs
' if we've got a photo, display it
If Not IsNothing(irec.Photo1) Then
RenderPhotoXML(Me.Page, irec, objX, 1)
End If
' only show more photos of the same item if we're not in single-view mode
If Not ShowSingle Then
' show second photo if we have one
If Not IsNothing(irec.Photo2) Then
RenderPhotoXML(Me.Page, irec, objX, 2)
End If
' show third photo if we have one
If Not IsNothing(irec.Photo3) Then
RenderPhotoXML(Me.Page, irec, objX, 3)
End If
' show fourth photo if we have one
If Not IsNothing(irec.Photo4) Then
RenderPhotoXML(Me.Page, irec, objX, 4)
End If
End If
Next
objX.WriteEndElement()
objX.WriteEndElement()
objX.WriteEndDocument()
objX.Flush()
objX.Close()
Response.End()
End Sub
' RenderPhotoXML is below, don’t forget to add it!
End Class
|
This sets the content type of the response stream to XML, builds an XML document, and then appends that document to web page’s response stream.
objX.WriteElementString adds XML elements to the document. You can see I’ve added "<title>", "<link>" "<description>", "<copyright>" and "<ttl>"
elements. The "<ttl>" element specifies the "Time to Live" of the feed. It’s basically saying, "This feed isn’t going to change for the next 15
minutes, so if you come back during that time you won’t notice any change." Smart RSS readers will then override attempts by users to get the information
more frequently.
The next bit of code fetches the most recent 10 items from the items table. In this example, I use the "LastUpdated" column in the items table.
If Emma updates the description or information on any of her existing bears, it will generate a new article in the feed.
Notice I look at the query string parameter "single". I do this because each item in this database has up to four photos. That’s handy for slideshows,
but if you’re just publishing the item description to a text-only news feed, you don’t want it to repeat four times.
Also notice that each time I add a photo and its description to the feed, I call the "RenderPhotoXML" function which is also part of the code-behind:
Public Sub RenderPhotoXML(ByVal MyPage As Page, ByVal iRec As ItemsRecord, _
ByVal objX As XmlTextWriter, ByVal PhotoNumber As Integer)
Dim x As New X1
' build a link to the item
Dim strURL As String = x.AppendToURL(Me.Page, "Items/ShowItems.aspx?Items=" & _
System.Web.HttpUtility.UrlEncode(iRec.GetID.ToXmlString()))
' build a link to the image
' note the & in the url. XML chokes on naked ampersands in some readers.
Dim strImageURL As String = x.AppendToURL(Me.Page, "Items/ShowImage.aspx?ItemID=" & _
CStr(iRec.ItemID)) & "&p=" & PhotoNumber
' start a new item
objX.WriteStartElement("item")
' make the title the item name
objX.WriteElementString("title", CStr(iRec.Name))
' add in the mediaRSS content
objX.WriteRaw("<media:group>")
objX.WriteRaw("<media:content")
objX.WriteRaw(" url=""" & strImageURL & """")
objX.WriteRaw(" type=""image/" & "jpeg" & """")
objX.WriteRaw(" medium=""image""")
objX.WriteRaw(" /> ")
objX.WriteRaw(vbNewLine)
objX.WriteElementString("media:title", CStr(iRec.Name))
objX.WriteElementString("media:description", strURL)
objX.WriteRaw(vbNewLine)
' note that in this example, the thumbnail is just the same the original image.
' For completeness, we should probably scale down the image size to thumbnail
' dimensions (say 50 x 50). Maybe next version?
objX.WriteStartElement("media:thumbnail")
objX.WriteAttributeString("url", strImageURL)
objX.WriteEndElement()
objX.WriteRaw(vbNewLine)
objX.WriteRaw("</media:group>")
objX.WriteRaw(vbNewLine)
' add in a normal <description> tag for normal RSS readers that don't understand
' the MediaRSS specification.
' this makes the feed useful for both RSS and MediaRSS readers
' build a <a href=[url]><img src=[url]/></a> construct
Dim strLinkedImageUrl As String = x.Linkify("<img src='" & strImageURL & "' />", strURL)
' output the <description> node for this item
objX.WriteElementString( _
"description", _
strLinkedImageUrl _
& "<br/>" _
& CStr(iRec.DescriptionHTML) _
& "<br/> On " _
& CDate(iRec.DateEntered).ToString("R"))
' compelte the rest of the stuff for this item
objX.WriteElementString("link", strURL)
objX.WriteElementString("pubDate", CDate(iRec.LastUpdated).ToString("R"))
objX.WriteEndElement()
objX.WriteRaw(vbNewLine)
End Sub
|
This method adds a new "<item>" element to the XML, and then populates it with information about the photo:
"<title>", "<media:group>", "<description>", "<link>" and "<pubDate>".
The "<media:group>" tag, and all tags starting with "<media:", are part of the Yahoo MediaRSS specification,
which will be sought by any readers who take advantage of MediaRSS.
I add the "<description>" tag as well for standard RSS readers who don’t know anything about MediaRSS.
The code-behind also uses the following utilities that I place in a separate X1 object. I use this in several different
projects. X1.vb lives in the app_code folder.
Imports Microsoft.VisualBasic
Imports System.Net
Imports System.IO
Imports System.Data
Public Class X1
Public Function AppendToURL(ByVal MyPage As Page, ByVal NewPage As String) As String
' strip out the current page, and add a new page to a url
Dim sUrl As String = MyPage.Request.Url.ToString
Dim x As Integer = sUrl.Length
While Mid(sUrl, x, 1) <> "/" And x > 0
x -= 1
End While
Return Left(sUrl, x) & NewPage
End Function
Public Function AppendToURL(ByVal MyPage As String, ByVal NewPage As String) As String
' strip out the current page, and add a new page to a url
Dim sUrl As String = MyPage
Dim x As Integer = sUrl.Length
While Mid(sUrl, x, 1) <> "/" And x > 0
x -= 1
End While
Return Left(sUrl, x) & NewPage
End Function
Public Function Linkify(ByVal strText As String, ByVal strUrl As String) As String
Return Linkify(strText, strUrl, 0, True)
End Function
Public Function Linkify(ByVal strText As String, ByVal strUrl As String, ByVal MaxLinkLength As Integer) _
As String
Return Linkify(strText, strUrl, MaxLinkLength, True)
End Function
Public Function Linkify(ByVal strText As String, ByVal strUrl As String, _
ByVal MaxLinkLength As Integer, ByVal isPopup As Boolean) As String
' add a link to text
'<a href="../html/find-a-cruise.cfm">Find A Cruise</a>
If MaxLinkLength <= 0 Or strText.Length <= MaxLinkLength Then
Return "<a href=" & strUrl & IIf(isPopup, " target=""_blank""", "") & " >" & strText & "</a>"
Else
strText &= " "
Dim ix As Integer = strText.IndexOfAny(New Char() {" ", ",", ".", "(", ")", "?", "!"}, _
MaxLinkLength)
If ix > 0 Then
Return "<a href=" & strUrl & IIf(isPopup, " target=""_blank""", "") & " >" & _
RTrim(Left(strText, ix)) & "</a>" & Mid(strText, ix + 1)
Else
Return "<a href=" & strUrl & IIf(isPopup, " target=""_blank""", "") & " >" & _
Left(strText, MaxLinkLength) & "</a>" & RTrim(Mid(strText, MaxLinkLength + 1))
End If
End If
End Function
End Class
|
Finally, notice the "<img src=[url/>" tag refers to a "ShowImage.aspx" page. This is so I can render a database image in the feed
via a unique URL. If I wanted, I could add more query string parameters to this page to allow scaling for thumbnails. For now, I’m just
going to use one image size.
Here’s the ShowImage.aspx page:
<%@ Page Language="VB" AutoEventWireup="false" CodeFile="ShowImage.aspx.vb" Inherits="Items_ShowImage" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Show Image</title>
</head>
<body>
<form id="form1" runat="server">
<div>
</div>
</form>
</body>
</html>
|
As you can see, it’s a plain ASP.NET page with nothing much on it. The Page_Load event does all the work.
Here’s the code-behind:
Option Strict On
Imports System.IO
Imports System.Net
Imports BaseClasses
Imports BaseClasses.Utils
Imports BaseClasses.Web.UI.WebControls
Imports Bears.Business
Imports Bears.Data
Partial Class Items_ShowImage
Inherits System.Web.UI.Page
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
' this page is for inserting images from the database into the response stream
' for retrieving items if necessary
Dim iRec As ItemsRecord = Nothing
' Cache images for up to 60 minutes.
' Recycling is kinder to the database server
Dim CacheKey As String = "ShowImage.aspx?"
CacheKey &= "ItemID=" & Request.QueryString("ItemID") & "&p=" & Request.QueryString("p")
' is this image in the cache?
If Cache(CacheKey) Is Nothing Then
' not in the cache
' get the item from the database
iRec = ItemsTable.GetRecord("ItemID=" & Request.QueryString("ItemID"))
' put it in the cache
Cache.Insert(CacheKey, iRec, Nothing, DateTime.Now.AddMinutes(60), TimeSpan.Zero)
Else
' it's in the cache, so recycle it.
iRec = CType(Cache(CacheKey), ItemsRecord)
End If
' have we got an item record?
If Not IsNothing(iRec) Then
' convert Image to a memory stream
Dim stream As MemoryStream = Nothing
' decide which photo to used, based on the "p" query string param
Select Case CInt(Request.QueryString("p"))
Case 1
stream = New MemoryStream(iRec.Photo1)
Case 2
stream = New MemoryStream(iRec.Photo2)
Case 3
stream = New MemoryStream(iRec.Photo3)
Case 4
stream = New MemoryStream(iRec.Photo4)
End Select
' put the stream into an image
Dim img As System.Drawing.Image = System.Drawing.Image.FromStream(stream)
' output as JPG
Response.ContentType = "image/jpeg"
img.Save(Response.OutputStream, System.Drawing.Imaging.ImageFormat.Jpeg)
End If
End Sub
End Class
|
This page retrieves the item record based on the parameters passed in the query string. It then copies the data from
that image into the response stream. The end result is that I can use ShowImage.aspx as the "src" of any "<img>" tag
that needs to display images from the database.
To reduce the load on the database, image requests are cached for up to an hour. Since I’m only displaying the most recent
10 items, I don’t need to go back to the database for them each time. It’s more efficient to keep them in a cache.
What does it look like? Here’s a sample of some of the XML data that gets output:
<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:media="http://search.yahoo.com/mrss/">
<channel>
<title>EmmasBears.com Picture Feed</title>
<link>http://EmmasBears.com/rss.aspx</link>
<description>Quality hand crafted Teddy Bears from EmmasBears.com</description>
<copyright>(c) 2009, Emma Braiden. All rights reserved.</copyright>
<ttl>15</ttl>
<item>
<title>Atticus and Rangle </title>
<media:group>
<media:content url="http://localhost:2067/Bears/Items/ShowImage.aspx?ItemID=4&p=1" type="image/jpeg" medium="image" />
<media:title>Atticus and Rangle </media:title>
<media:description>http://localhost:2067/Bears/Items/ShowItems.aspx?Items=%3ckey%3e%3ccv%3e%3cc%3eName%3c%2fc%3e%3cv%3eAtticus+and+Rangle+%3c%2fv%3e%3c%2fcv%3e%3c%2fkey%3e</media:description>
<media:thumbnail url="http://localhost:2067/Bears/Items/ShowImage.aspx?ItemID=4&p=1" />
</media:group>
<description><a href=http://localhost:2067/Bears/Items/ShowItems.aspx?Items=%3ckey%3e%3ccv%3e%3cc%3eName%3c%2fc%3e%3cv%3eAtticus+and+Rangle+%3c%2fv%3e%3c%2fcv%3e%3c%2fkey%3e target="_blank" ><img src='http://localhost:2067/Bears/Items/ShowImage.aspx?ItemID=4&p=1' /></a><br/><p>Just showing Atticus and Rangle snuggled together.<br /><br />(Atticus is adopted - Rangles is currently still available) .<br /> </p><br/> On Sat, 18 Apr 2009 10:31:09 GMT</description>
<link>http://localhost:2067/Bears/Items/ShowItems.aspx?Items=%3ckey%3e%3ccv%3e%3cc%3eName%3c%2fc%3e%3cv%3eAtticus+and+Rangle+%3c%2fv%3e%3c%2fcv%3e%3c%2fkey%3e</link>
<pubDate>Wed, 20 May 2009 20:11:13 GMT</pubDate>
</item>
<item>
<title>Bethany </title>
<media:group>
<media:content url="http://localhost:2067/Bears/Items/ShowImage.aspx?ItemID=6&p=1" type="image/jpeg" medium="image" />
<media:title>Bethany </media:title>
<media:description>http://localhost:2067/Bears/Items/ShowItems.aspx?Items=%3ckey%3e%3ccv%3e%3cc%3eName%3c%2fc%3e%3cv%3eBethany+%3c%2fv%3e%3c%2fcv%3e%3c%2fkey%3e</media:description>
<media:thumbnail url="http://localhost:2067/Bears/Items/ShowImage.aspx?ItemID=6&p=1" />
</media:group>
<description><a href=http://localhost:2067/Bears/Items/ShowItems.aspx?Items=%3ckey%3e%3ccv%3e%3cc%3eName%3c%2fc%3e%3cv%3eBethany+%3c%2fv%3e%3c%2fcv%3e%3c%2fkey%3e target="_blank" ><img src='http://localhost:2067/Bears/Items/ShowImage.aspx?ItemID=6&p=1' /></a><br/><p>Meet Bethany... <br /><br />Bethany is a lovely big girl made from a luxurious rich old-<br />gold brown dense curly mohair with a real shine to the pile. <br />She has big black deluxe glass eyes and ultra-suede whites. <br /><br />Bethany is fully and firmly lock-nut jointed, and stuffed <br />quite firmly with a heap of polyfill making her rather chunky <br />and naturally heavy (no need for weighting!). <br /><br />Bethany has black faux suede paw pads to match her over-sized <br />hand embroidered black perle thread nose. She wears a simple <br />baby-blue satin wire-edged ribbon bow around her head. <br /><br />Bethany has a little rag doll friend that comes with her as <br />well, it has been handmade by another Australian artist. <br /><br />Bethany is a one of a kind bear. Her adoption fee includes <br />registered post anywhere in the world, so no nasty surprises <br />of massive postage amounts due to her large size and weight. <br /> </p><br/> On Sun, 19 Apr 2009 11:33:47 GMT</description>
<link>http://localhost:2067/Bears/Items/ShowItems.aspx?Items=%3ckey%3e%3ccv%3e%3cc%3eName%3c%2fc%3e%3cv%3eBethany+%3c%2fv%3e%3c%2fcv%3e%3c%2fkey%3e</link>
<pubDate>Wed, 20 May 2009 19:57:01 GMT</pubDate>
</item>
... some data was deleted to improve readability...
</channel>
</rss>
|
|