The XMLHttpRequest object
The XMLHttpRequest object has been updated several times since was defined as the WHATWG’s HTML effort using Microsoft technology; then we had the original XMLHttpRequest Level 1 specification as part of W3C, we also had the XMLHttpRequest Level 2 updated specification, and now we have the last version of this object known as XMLHttpRequest Living Specification. We can summarize its advantages in the following points:
- Allows upload and download files as stream bytes, large binaries (BLOBs) or data forms
- It has event handlers for progress, errors, abortion, start, and end of operations
- Cross-domain capabilities (CORS)
- New type for JSON responses
- Is a fundamental part of the HTML5 File API specification
It’s important to emphasize that before HTML5 and the latest versions of XMLHttpRequest object it was required to resort to server-side technology in order to able to perform an uploading file operation, that is, it wasn’t possible to upload a file natively from the client side. Technologies as AJAX and Flash did their own effort but with serious limitations, so XMLHttpRequest comes to cover this old problem in a big way. There are other additional features that come with XMLHttpRequest, if you want to know more you can resort to the official specification.
Starting
The first thing we’ll do is to define the user interface for this small implementation starting with the HTML tags, the code is very simple and only includes some form elements, and some div tags that are only used to give a better presentation using CSS3. I won’t analyze in this post anything related to the used cascade style sheets since is not something really necessary for the operation of this example.
<!DOCTYPE html> <html> <head> <title>Upload File</title> <meta charset="iso-8859-1" /> </head> <body> <div id="wrap"> <div class="field"> <ul class="options"> <li><input type="file" id="myfile" name="myfile" class="rm-input" onchange="selectedFile();"/></li> <li><div id="fileSize"></div></li> <li><div id="fileType"></div></li> <li><input type="button" value="Subir Archivo" onClick="uploadFile()" class="rm-button" /></li> </ul> </div> <progress id="progressBar" value="0" max="100" class="rm-progress"></progress> <div id="percentageCalc"></div> </div> </body> </html>
- A file-type input which will be used to select the file to be uploaded
- A div which will be used to print the size of the selected file
- A div which will be used to print the MIME type of the selected file
- A button which will fire the uploading process for the selected file
- A progress bar to indicate the uploading process progress of the selected file
- Finally, a div where the progress will be shown in a percentage format
The selectedFile() function
function selectedFile() { var archivoSeleccionado = document.getElementById("myfile"); var file = archivoSeleccionado.files[0]; if (file) { var fileSize = 0; if (file.size > 1048576) fileSize = (Math.round(file.size * 100 / 1048576) / 100).toString() + ' MB'; else fileSize = (Math.round(file.size * 100 / 1024) / 100).toString() + ' Kb'; var divfileSize = document.getElementById('fileSize'); var divfileType = document.getElementById('fileType'); divfileSize.innerHTML = 'Tamaño: ' + fileSize; divfileType.innerHTML = 'Tipo: ' + file.type; } }
The uploadFile() function
Esta es la función que hace un mayor uso de las nuevas posibilidades de XMLHttpRequest , y es la que se encargará de disparar el proceso principal del lado cliente.
function uploadFile(){ //var url = "/ReadMoveWebServices/WSUploadFile.asmx/UploadFile"; var url = "/ReadMoveWebSite/UploadMinimal.aspx"; var archivoSeleccionado = document.getElementById("myfile"); var file = archivoSeleccionado.files[0]; var fd = new FormData(); fd.append("archivo", file); var xmlHTTP = new XMLHttpRequest(); //xmlHTTP.upload.addEventListener("loadstart", loadStartFunction, false); xmlHTTP.upload.addEventListener("progress", progressFunction, false); xmlHTTP.addEventListener("load", transferCompleteFunction, false); xmlHTTP.addEventListener("error", uploadFailed, false); xmlHTTP.addEventListener("abort", uploadCanceled, false); xmlHTTP.open("POST", url, true); //xmlHTTP.setRequestHeader('book_id','10'); xmlHTTP.send(fd); }
//var url = "/ReadMoveWebServices/WSUploadFile.asmx/UploadFile"; var url = "/ReadMoveWebSite/UploadMinimal.aspx"; var archivoSeleccionado = document.getElementById("myfile"); var file = archivoSeleccionado.files[0]; var fd = new FormData(); fd.append("archivo", file);
Event handlers
- loadstart. Is triggered when the uploading file process initiates.
- progress. Is triggered each time there is an advance in the file uploading process.
- load. Is triggered when the transfer is complete successfully.
- error. Is triggered when the transfer fails
- abort. Is triggered when the user/developer interrupts the process.
var xmlHTTP= new XMLHttpRequest(); //xmlHTTP.upload.addEventListener("loadstart", loadStartFunction, false); xmlHTTP.upload.addEventListener("progress", progressFunction, false); xmlHTTP.addEventListener("load", transferCompleteFunction, false); xmlHTTP.addEventListener("error", uploadFailed, false); xmlHTTP.addEventListener("abort", uploadCanceled, false);
The triggered events functions are the following:
function progressFunction(evt){ var progressBar = document.getElementById("progressBar"); var percentageDiv = document.getElementById("percentageCalc"); if (evt.lengthComputable) { progressBar.max = evt.total; progressBar.value = evt.loaded; percentageDiv.innerHTML = Math.round(evt.loaded / evt.total * 100) + "%"; } } function loadStartFunction(evt){ alert('Comenzando a subir el archivo'); } function transferCompleteFunction(evt){ alert('Transferencia completa'); var progressBar = document.getElementById("progressBar"); var percentageDiv = document.getElementById("percentageCalc"); progressBar.value = 100; percentageDiv.innerHTML = "100%"; } function uploadFailed(evt) { alert("Hubo un error al subir el archivo."); } function uploadCanceled(evt) { alert("La operación se canceló o la conexión fue interrunpida."); }
ProgressFunction() updates the progress bar and percentage which indicate in a graphical and numerical way the process progress, the rest of the functions only display the proper message for each case.
Commented code
If you have observed the code you probably noticed some commented lines, this is because this is just the base code to create something a little bit more complex, but I decided to leave those lines because maybe can be useful for someone:
//var url = "/ReadMoveWebServices/WSUploadFile.asmx/UploadFile";
The previous line of code is a call to a .Net HTTP service instead of a page. Here is where you should call your own server-side implementation.
//xmlHTTP.upload.addEventListener("loadstart", loadStartFunction, false);
This line calls a function that shows a message when the process starts. I commented this line after I executed the code several times because was annoying.
The completo code
This is how the complete implementation of the code looks:
<!DOCTYPE html> <html> <head> <title>Upload File</title> <meta charset="iso-8859-1" /> <link rel="stylesheet" type="text/css" href="estilosUploadFile.css" /> <script type="text/javascript"> function selectedFile() { var archivoSeleccionado = document.getElementById("myfile"); var file = archivoSeleccionado.files[0]; if (file) { var fileSize = 0; if (file.size > 1048576) fileSize = (Math.round(file.size * 100 / 1048576) / 100).toString() + ' MB'; else fileSize = (Math.round(file.size * 100 / 1024) / 100).toString() + ' Kb'; var divfileSize = document.getElementById('fileSize'); var divfileType = document.getElementById('fileType'); divfileSize.innerHTML = 'Tamaño: ' + fileSize; divfileType.innerHTML = 'Tipo: ' + file.type; } } function uploadFile(){ //var url = "http://localhost/ReadMoveWebServices/WSUploadFile.asmx?op=UploadFile"; var url = "/ReadMoveWebServices/WSUploadFile.asmx/UploadFile"; var archivoSeleccionado = document.getElementById("myfile"); var file = archivoSeleccionado.files[0]; var fd = new FormData(); fd.append("archivo", file); var xmlHTTP= new XMLHttpRequest(); //xmlHTTP.upload.addEventListener("loadstart", loadStartFunction, false); xmlHTTP.upload.addEventListener("progress", progressFunction, false); xmlHTTP.addEventListener("load", transferCompleteFunction, false); xmlHTTP.addEventListener("error", uploadFailed, false); xmlHTTP.addEventListener("abort", uploadCanceled, false); xmlHTTP.open("POST", url, true); //xmlHTTP.setRequestHeader('book_id','10'); xmlHTTP.send(fd); } function progressFunction(evt){ var progressBar = document.getElementById("progressBar"); var percentageDiv = document.getElementById("percentageCalc"); if (evt.lengthComputable) { progressBar.max = evt.total; progressBar.value = evt.loaded; percentageDiv.innerHTML = Math.round(evt.loaded / evt.total * 100) + "%"; } } function loadStartFunction(evt){ alert('Comenzando a subir el archivo'); } function transferCompleteFunction(evt){ alert('Transferencia completa'); var progressBar = document.getElementById("progressBar"); var percentageDiv = document.getElementById("percentageCalc"); progressBar.value = 100; percentageDiv.innerHTML = "100%"; } function uploadFailed(evt) { alert("Hubo un error al subir el archivo."); } function uploadCanceled(evt) { alert("La operación se canceló o la conexión fue interrunpida."); } </script> </head> <body> <div id="wrap"> <div class="field"> <ul class="options"> <li> <input type="file" id="myfile" name="myfile" class="rm-input" onchange="selectedFile();"/> </li> <li> <div id="fileSize"></div></li> <li> <div id="fileType"></div></li> <li> <input type="button" value="Subir Archivo" onClick="uploadFile()" class="rm-button" /></li> </ul> </div> <progress id="progressBar" value="0" max="100" class="rm-progress"></progress> <div id="percentageCalc"></div> </div> </body> </html>
I’m not describing the CSS3 code because is irrelevant in terms of functionality, but I share an image that shows how it looks the implementation in the browser and the link to the CSS3 estilosUploadFile.zip.
I’m also sharing the original HTTP service that I used to test this example -the server-side code, backend file or any name that you prefer 😃- but this won’t be very useful for you unless you use the exact same stack that I was using at the moment- in other words, if you are the kind of person who just wants to copy and paste….hehehe well….maybe you’re not ready for this yet. Here is the file WSUploadFile.zip
Sorry about my English I’m not a natural speaker (don’t be grumpy, help me to improve).
This is all for now folks, I hope this can be useful for you.
Here some books that can help you in your HTML5 journey:
Emmanuel Herrera
IT professional with several years of experience in management and systems development with different goals within public and private sectors.
Emmanuel worked through development and management layers, transitioning from developer and team development leader to Project Manager, Project Coordinator, and eventually to Scrum Master, Product Owner, and Agile Coach.
Some certifications include: PSM, PSPO, SSM.
Hola Emmanuel buen dia,
una pregunta, en esta linea “var url = “/ReadMoveWebServices/WSUploadFile.asmx/UploadFile”;” es donde se define la ubicación en la cual se guarda el archivo subido?
Gracias
Hola. Esa línea es donde se llama al webservice WSUploadFile.asmx y su método UploadFile, dentro del código de ese webservice (archivo WSUploadFile.asmx.cs link al final de la entrada) se define la ruta, es este caso en particulaer hay líneas con la variable llamada BaseDirectory a las cuales se le asigna la ruta usando el objeto Server.MapPath( ‘direcorio’ ); pero esto cambiará dependiendo de que tecnología utilices.
Hallo,
work fine, just a problem. I can’t find the uploaded files!!
I’m using apache/php server i suppose to find it in the document root…. i’m wrong?
Thank’s
You set the upload folder in your server-side code.