Introduction
There is no easier way to share files online than to upload them in your web space and provide others with the right links. Document files, spreadsheets, images, and MP3s are all small in size, and can be easily uploaded. But, what if you want to share a big bunch of files, MPG video files, for example? If you are unwilling to FTP 100MB to your web host, then you still have an alternative: host them yourself and provide links to the Web Server running at your computer. All you need is a static IP and the IIS (or any other web server) installed and running. There is one last caveat in this scenario. No matter how long your home computer can stay online, it will eventually be shutdown, which leads to broken links. Whether you turn off your computer for 5 minutes or 5 hours, broken links are always a nuisance. This is where HomeLinks come as a means of protection against broken links and annoyed web users! Plus, HomeLinks is a showcase for asynchronous, client-side JavaScript calls to XML Web Services!
Background
Before diving into the code, there are a few setup requirements to be met:
- You must have a Web Server running in your computer. The local folder where the homelinks.xml file and the other files you want to share online reside must be made publicly available through your web server. You can either create a virtual directory, or place the folder under the Web Server's root. In either case, the following test URL must be accessible:
http://127.0.0.1/(VIRTUAL) DIRECTORY NAME
- Your Web Server must be reachable from outside your LAN. If your computer is behind a router/firewall, then you have to configure it in order to allow access to port 80 and forward incoming HTTP calls to the Web Server. Make sure you open port 80 in your (Windows) firewall settings too!
- You better have a static IP even though it is not mandatory. You can always use your current IP, dynamically assigned to you by your ISP, to run the sample application demonstrated here. Or, you can assign your dynamic IP to an automatically updated domain name so people can always access your Web Server. This can be done using free services such as DynDNS that allow you to select your domain name of preference, associate it with your dynamic IP, and have it updated automatically whenever your ISP changes your IP. In our case, the Web Server listens to the following dynamically assigned domain name no matter what my IP is:
http://dpant.homeip.net
and people can access our sample "rally_mavro_rodo" virtual directory using the URL:
http://dpant.homeip.net/rally_mavro_rodo
- Apart from the homelinks.xml file, all other source files included in the source Zip file must be placed under a HomeLinks ASP.NET Web Application in your remote web host. If you have IIS installed locally, you can of course run the Web Application in your computer, but make sure you include the right URLs inside the homelinks.xml file (e.g., pseudo-static domain names won't work; use your local IP instead). Since the Web Application facilitates a Web Service, make sure you place the following lines in the web.config file:
<system.web>
...
<webServices>
<protocols>
<add name="HttpGet"/>
<add name="HttpPost"/>
</protocols>
</webServices>
</system.web>
Using the code
In Figure 1, you can see the HomeLinks architecture. HomeLinks is made up of the following components:
- An XML Web Service (HomeLinksService.asmx).
- The homelinks.xml file.
- The service consumer (HomeLinks.htm or ShowHomeLinks.htm in the source Zip file).
Figure 1: The HomeLinks architecture
homelinks.xml
This file feeds the XML Web Service with the links that will be displayed to the end user. In other words, the HLink
nodes are translated into HTML hyperlinks by the Web Service. The homelinks.xml file resides in the web-shared folder in your computer, so you can easily decide on which files will be available online by adding or removing HLink
nodes:
="1.0"="utf-8"
<HomeLinks>
<HLink href="http://dpant.homeip.net/rally_mavro_rodo/MOV02573.MPG"
target="_blank">Blaxokerasia 1 (2,4Mb)</HLink>
<HLink href="http://dpant.homeip.net/rally_mavro_rodo/MOV02578.MPG"
target="_blank">Blaxokerasia 2 (1,6Mb)</HLink>
<HLink href="http://dpant.homeip.net/rally_mavro_rodo/MOV02579A.WMV"
target="_blank">Blaxokerasia 3 (1.9Mb)</HLink>
...
<HomeLinks>
The XML Web Service
In order to use the Web Service, we call the HomeLinks
method which accepts one parameter: the URI where the homelinks.xml file is located. In our example, we provide the http://dpant.homeip.net/rally_mavro_rodo URL. The Web Service tries to access the homelinks.xml file, uses the MSXML parser to read the HLink
nodes, and creates and returns an XML-formatted document of HTML A links. For example, the first HLink
node above results in this HTML link:
<a href="http://dpant.homeip.net/rally_mavro_rodo/MOV02573.MPG"
target="_blank">Blaxokerasia 1 (2,4Mb)</a>
If the homelinks.xml file is unreachable (your computer is shutdown, offline, or your Web Server is stopped), the Web Service simply returns an error message (NO_HOMELINKS
) stating the fact. In this way, we avoid presenting the users with broken links. The XML Web Service is written in C#. The HomeLinks
method uses MSXML on the server-side by importing the System.Xml
.NET namespace. The Load
method of the XmlDocument
object is used to access the XML document, the documentElement
property represents the root element of the XML file loaded in memory, and the SelectNodes
method allows us to apply XPath Expressions to XML data:
public class HomeLinksService : System.Web.Services.WebService {
const string NO_HOMELINKS =
"The {0} URI is currently unreachable (probably offline)";
const string BR = "<br/>";
const string A = "<a href='{0}' target='{1}'>{2}</a>";
[WebMethod]
public string HomeLinks(string URI) {
XmlDocument hlDoc = new XmlDocument();
try
{
string links = String.Empty;
hlDoc.Load(FixURI(URI) + "homelinks.xml");
XmlNodeList hlinks = hlDoc.DocumentElement.SelectNodes("HLink");
for (int i = 0; i < hlinks.Count; i++)
{
links += String.Format(A, hlinks[i].Attributes["href"].Value,
><>hlinks[i].Attributes["target"].Value, hlinks[i].InnerText) + BR;
}
return links;
}
catch (Exception) {
return String.Format(NO_HOMELINKS, URI);
}
}
}
The service consumer
Figure 1 shows two possible Web Service consumers. The HomeLinks.htm (or HomeLinks.htm in the source Zip file) file can be either part of the web application (Consumer A) or can be located anywhere else on the Internet (Consumer B). Unfortunately, Consumer B is restrained by security issues of the XmlHttpRequest
object, and will only work with Internet Explorer 6. Either way, we access the Web Service using the XmlHttpRequest
object through JavaScript. This is an asynchronous, client-side, GET HTTP call to a Web Server resource. This can be anything from a simple text or HTML file to an ASPX/PHP/Java/CGI script. The onreadystatechange
callback function is called when a result is returned by the server. Web Services always return results in XML format. Pay attention to the readyState
and status
properties of the XmlHttpRequest
object. They indicate whether the asynchronous call was successful or not. Last but not least, please note that the XmlHttpRequest
object instance creation is browser-dependent. Here is the full script code:
<script type="text/javascript">
var req = false;
function getHomeLinks() {
if (window.ActiveXObject)
req = new ActiveXObject("Msxml2.XMLHTTP");
else if (window.XMLHttpRequest)
req = new XMLHttpRequest();
if (req) {
req.open("GET", "HomeLinksService.asmx/HomeLinks?" +
"URI=http://dpant.homeip.net/rally_mavro_rodo", true);
req.onreadystatechange = function() {
if (req.readyState == 4) {
if (req.status == 200)
document.getElementById("divHL").innerHTML =
req.responseXML.documentElement.firstChild.nodeValue;
else
document.getElementById("divHL").innerHTML = "readyState = " +
req.readyState + ", status = " +
req.status + " (" + req.statusText + ")";
document.getElementById("divWait").style.visibility = "hidden";
}
else {
document.getElementById("divWait").style.visibility = "visible";
}
}
req.send(null);
}
else {
document.write("Unable to create the XMLHttpRequest object.");
}
}
</script>
and here is the call within HomeLinks.htm (or ShowHomeLinks.htm in the source Zip file):
<body onload="getHomeLinks(); return true;">
<div>
<div id="divHL"></div>
<div id="divWait"
style="left: 50%; top: 50%; margin-top: 222px;
margin-left: -113px; position: absolute; visibility: hidden;
vertical-align: middle; width: 226px;
color: #3366cc; height: 111px; background-color: #ffff99;
text-align: center; border-right: #3333cc 1px solid;
border-top: #3333cc 1px solid; border-left: #3333cc 1px solid;
border-bottom: #3333cc 1px solid; top: 0px;">
<br /><br /> HomeLinks loading, Please wait ...
</div>
</div>
</body>
Sample Application Results
In our example, the hyperlinks point to a bunch of MPG video files (with funny, Greek names) taken from a Rally race event. You can check them out in my blog post. Of course, the HomeLinks will be available only if my computer is up and running ;)
Here are two screenshots of our sample application:
Screenshot 1: Successful execution
Screenshot 2: HomeLinks as a means to prevent broken links
Points of interest
How to debug
It is most certain that it will take you quite a while to debug this project. Web Services and the XmlHttpRequest
object can prove to be a real big pain especially with all the hideous error messages and no debugging tools. There are also security and configuration issues that will block your way to success. Here, I list the most common pitfalls you will encounter:
XmlHttpRequest returns Internal Server Error (status = 500)
XmlHttpRequest returns Not Found (status = 404)
- Make sure you use the correct URL to the .asmx file.
- The Web Service method calls are case sensitive. In our example, type HomeLinks, and not homeLinks or homelinks.
JavaScript error: uncaught exception: Permission denied to call method XmlHttpRequest.open
- Make sure you place the consumer file in the same domain as the Web Service. This is a security constraint imposed by browsers. Internet Explorer will warn you, but will finally make the call unlike Mozilla Firefox.
JavaScript returns nothing
- Make sure you display the
readyState
, status
, and statusText
values. Do not use document.write
for this purpose as this is an asynchronous call and the latest results will override the previous ones. Use DIV
or TEXTAREA
objects instead. - Make sure you display the XML response inside a
DIV
or TEXTAREA
object. Do not use document.write
for this purpose. - Make sure you handle the XML document correctly.
- Use the Firefox Error Console to check for coding errors.