<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>ZedBlog &#187; FileReader</title>
	<atom:link href="http://blog.zedplan.com/tag/filereader/feed" rel="self" type="application/rss+xml" />
	<link>http://blog.zedplan.com</link>
	<description></description>
	<lastBuildDate>Mon, 31 May 2010 14:57:21 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.1</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Upload de archivos con barra de progreso usando XMLHttpRequest y la nueva API de HTML5</title>
		<link>http://blog.zedplan.com/upload-de-archivos-con-barra-de-progreso-usando-xmlhttprequest-y-la-nueva-api-de-html5/117</link>
		<comments>http://blog.zedplan.com/upload-de-archivos-con-barra-de-progreso-usando-xmlhttprequest-y-la-nueva-api-de-html5/117#comments</comments>
		<pubDate>Mon, 15 Feb 2010 18:12:14 +0000</pubDate>
		<dc:creator>Demián Rodríguez</dc:creator>
				<category><![CDATA[AJAX]]></category>
		<category><![CDATA[Desarrollo Web]]></category>
		<category><![CDATA[Javascript]]></category>
		<category><![CDATA[API]]></category>
		<category><![CDATA[DOM]]></category>
		<category><![CDATA[FileReader]]></category>
		<category><![CDATA[HTML5]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[XMLHttpRequest]]></category>

		<guid isPermaLink="false">http://blog.zedplan.com/?p=117</guid>
		<description><![CDATA[Creo que para muchos ha sido un desafío implementar un upload de archivos con barra de progreso ya que se debía enviar el formulario a un iframe oculto y mientras tanto revisar el progreso haciendo peticiones AJAX en pequeños intervalos. A esto había que sumarle todo el proceso del lado del servidor para calcular el [...]]]></description>
			<content:encoded><![CDATA[<p>Creo que para muchos ha sido un desafío implementar un upload de archivos con barra de progreso ya que se debía enviar el formulario a un iframe oculto y mientras tanto revisar el progreso haciendo peticiones AJAX en pequeños intervalos. A esto había que sumarle todo el proceso del lado del servidor para calcular el porcentaje, y no todos los lenguajes nos proveen esa información.</p>
<p>Si bien la posibilidad de leer archivos ya existía en Firefox 3, combinado con las nuevas características de la última versión de este navegador, se pueden implementar funcionalidades mas avanzadas.</p>
<p>Con la nueva API podemos leer archivos del lado del cliente y de este modo enviar el contenido binario mediante una petición usando el nuevo método <code>sendAsBinary()</code> del objeto  <code>XMLHttpRequest</code>.</p>
<p>Para acceder a los archivos, tenemos la propiedad <code>files</code> disponible en los elementos <code>input</code> de tipo <code>file</code> y ademas en el objeto <a href="https://developer.mozilla.org/En/DragDrop/DataTransfer" target="_blank"><code>DataTransfer</code></a> (accesible en las operaciones de drag &amp; drop). Esta propiedad es un objeto de tipo <code><a href="https://developer.mozilla.org/en/DOM/FileList" target="_blank">FileList</a></code> que representa una colección de objetos de tipo <a href="https://developer.mozilla.org/en/DOM/File" target="_blank"><code>File</code></a>. Un objeto <code>File</code> dispone de propiedades que proveen información básica del archivo, como el nombre, el tipo y el tamaño.</p>
<p>Gecko 1.9.2 imlementa el objeto <a href="https://developer.mozilla.org/en/DOM/FileReader" target="_blank"><code>FileReader</code></a>, el cual nos permite leer archivos en forma asincrónica y asi evitar que el navegador no responda durante la operación de lectura.</p>
<p>Si bien tenemos la posibilidad de enviar el contenido de un archivo como binario, para enviar múltiples archivos debemos generar el POST tal cual lo haría un formulario con el atributo <code>enctype="multipart/form-data"</code>.</p>
<p>Pueden ver en acción <a href="http://www.zedplan.com/es/demos/ajax-upload" target="_blank">un ejemplo muy sencillo</a> &#8211; basado en jQuery &#8211; que permite seleccionar multiples archivos y enviarlos en una sola petición. Se puede ver el progreso mediante una barra. Del lado del server, con PHP hago un <code>print_r()</code> con el contenido de la variable <code>$_FILES</code>. Lo pueden ver con el Firebug.</p>
<p>Veamos paso a paso el código Javascript del ejemplo.</p>
<pre>upload = [];

j(function() {
	j('#upload-progressbar').progressbar();

	j('#files').bind('change', function(e) {
		var files = e.target.files;
		var fileCount = 0;
		j.each(files, function(k, v) {
			fileCount++;
			reader = new FileReader();
			reader.onloadend = function(e) {
				upload.push({
					name : v.name,
					type : v.type || 'text/plain',
					bin : e.target.result // el contenido del archivo
				});
				if (upload.length == fileCount) uploadFiles();
			};
			reader.readAsBinaryString(v);
		});
	});
});
</pre>
<p>Al cargar la página, agregamos el evento <code>change</code> para el <code>input</code>. Cuando el usuario selecciona los archivos, iteramos por cada uno de ellos y leemos su contenido usando el metodo <code>readAsBinaryString()</code> del objeto <code>FileReader</code>. Al ser asincrónico, debemos utilizar el evento <code>onloadend</code> para detectar cuando terminó la operación. Dentro de esta función creamos un objeto con algunas propiedades del archivo y guardamos una referencia en un array global. Cuando detectamos que se han leído todos los archivos, procedemos a enviar el formulario:</p>
<pre>function uploadFiles() {
	var xhr = new XMLHttpRequest();

	j('#upload-progressbar').show();

	xhr.upload.addEventListener('progress', function(e) {
		if (e.lengthComputable) {
			var perc = Math.round((e.loaded * 100) / e.total);
			j('#upload-progressbar').progressbar('value', perc);
		}
	}, false);
	xhr.upload.addEventListener('load', function(e) {
		j('#upload-progressbar').progressbar('value', 100);
	}, false);

	xhr.open("POST", '/es/demos/ajax-upload');

	var BOUNDARY = '---------------------------1966284435497298061834782736';
	var rn = "\r\n";
	var req = '';
	j.each(upload, function(k, v) {
		req += "--" + BOUNDARY + rn + "Content-Disposition: form-data; name=\"files[]\"";
		req += '; filename="' + v.name + '"' + rn + 'Content-type: ' + v.type;
		req += rn + rn + v.bin + rn;
	});
	req += "--" + BOUNDARY + '--';

	xhr.setRequestHeader("Content-Type", "multipart/form-data; boundary=" + BOUNDARY);
	xhr.sendAsBinary(req);
}
</pre>
<p>Creamos un objeto <code>XMLHttpRequest</code>. Aquí vemos que <code>xhr.upload</code> hace referencia a un objeto que contiene información sobre el proceso de upload y nos ofrece algunos eventos útiles. Abrimos una conección para enviar el formulario por POST y luego generamos el contenido binario que recibiremos en el servidor. Debemos establecer que el <code>Content-Type</code> es de tipo <code>multipart/form-data</code>, y como indica el nombre, quiere decir que la petición esta formada por múltiples partes. Para separarlas se utiliza una cadena que no aparezca dentro del contenido de los archivos (si estan aburridos pueden leer mas detalles <a href="http://www.faqs.org/rfcs/rfc1867.html" target="_blank">aquí</a>). En este caso es la variable <code>BOUNDARY</code>. Finalmente, la magia ocurre en el método <code>sendAsBinary()</code>. Pueden ver mas detalladamente el contenido del POST con el Firebug.</p>
<p>Esta técnica nos permite, por ejemplo, hacer una vista previa de las imágenes en el cliente o redimensionarlas antes de subirlas.</p>
<p>Quizas piensen en la seguridad como yo e intenten hacer esto:</p>
<pre>j('#files').val('/etc/passwd');
</pre>
<p>Pero <span style="text-decoration: line-through;">desgraciadamente</span> afortunadamente nos encontramos con un error de seguridad <img src='http://blog.zedplan.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p>No he investigado mucho en cuanto a compatibilidad en distintos navegadores. Si alguien tiene información al respecto es bienvenida.</p>
<p>UPDATE: El comportamiento en Firefox 3.6.2 sobre Ubuntu es algo inestable. A veces envía la petición pero se queda esperando una respuesta indefinidamente, y al seleccionar varios archivos se cierra el navegador de forma inesperada.</p>
<a class="a2a_dd addtoany_share_save" href="http://www.addtoany.com/share_save?linkurl=http%3A%2F%2Fblog.zedplan.com%2Fupload-de-archivos-con-barra-de-progreso-usando-xmlhttprequest-y-la-nueva-api-de-html5%2F117&amp;linkname=Upload%20de%20archivos%20con%20barra%20de%20progreso%20usando%20XMLHttpRequest%20y%20la%20nueva%20API%20de%20HTML5"><img src="http://blog.zedplan.com/wp-content/plugins/add-to-any/share_save_171_16.png" width="171" height="16" alt="Share/Bookmark"/></a>]]></content:encoded>
			<wfw:commentRss>http://blog.zedplan.com/upload-de-archivos-con-barra-de-progreso-usando-xmlhttprequest-y-la-nueva-api-de-html5/117/feed</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
	</channel>
</rss>
