Debido a algunos comentarios que recibí en la entrada “Enviar y/o subir multiples archivos adjuntos al estilo GMail” (varios se perdieron por problemas con el hosting) y algunos mails que he recibido, mostraré la forma de subir múltiples archivos con .NET.

Primero, veremos la forma de generar elementos HTML input de tipo file, para esto utilizo una versión mejorada del script que publiqué hace tiempo:

// Tomado de http://www.quirksmode.org/blog/archives/2005/10/_and_the_winner_1.html
addEvent = function ( obj, type, fn ) {
	if (obj.addEventListener)
		obj.addEventListener( type, fn, false );
	else if (obj.attachEvent) {
		obj["e"+type+fn] = fn;
		obj[type+fn] = function() { obj["e"+type+fn]( window.event ); };
		obj.attachEvent( "on"+type, obj[type+fn] );
	}
}
bc_newElement = function (tag) {
   return document.createElement(tag);
}
bc_getElement = function (id) {
   return document.getElementById(id);
}

var field_count = 1;

bc_init = function(fileId, displayId) {
    try {
        field = bc_getElement(fileId);
        field.display=bc_getElement(displayId);

        if (!field || !field.type || field.type != 'file' || !field.display) return;

        addEvent(field, 'change', bc_addField);
    } catch ( ex ) { bc_handleError(ex); }
}

bc_load = function (fileId, displayId) {
    addEvent(window, 'load', new Function("bc_init('" + fileId + "', '" + displayId + "');"));
}
// Basado en http://the-stickman.com/web-development/javascript/updated-upload-multiple-files-with-a-single-file-element/
bc_addField = function() {
    try {
        new_field = bc_newElement('INPUT');
        new_field.type = 'file';
        new_field.id = new_field.name = this.id.replace(/-@bc-.*$/g, "") + '-@bc-' + field_count++;
        new_field.display = this.display;
        addEvent(new_field, 'change', bc_addField);

        this.parentNode.insertBefore(new_field, this);

        li = bc_newElement('LI');

        a = bc_newElement('A');
        a.href="#";
        a.appendChild(document.createTextNode('Quitar'));
        a.field_id = this.id;
        addEvent(a, 'click', bc_removeField);

        li.appendChild(a);
        li.appendChild(document.createTextNode( this.value.substring( this.value.search(/[^\/\\]+$/) ) ));
        this.display.appendChild(li);

        this.style.position = 'absolute';
	    this.style.left = '-1000px';
    } catch ( ex ) { bc_handleError(ex); }
}
bc_removeField = function (event) {
    try {
        (del = bc_getElement(this.field_id)).parentNode.removeChild(del);

        this.parentNode.parentNode.removeChild(this.parentNode);
        if (event && event.preventDefault)
            event.preventDefault();
        return false;
    } catch ( ex ) { bc_handleError(ex); }
}
bc_handleError = function (ex) { alert ( ex ); }

Para registrar el script mostrado arriba, se puede hacer tanto desde el HTML (ver ejemplo) así como también desde código del lado del servidor, para el último caso se puede hacer en el evento Page_Load o en el evento PreRender, de la siguiente manera:

protected override void OnPreRender(EventArgs e)
{
	if(!Page.IsClientScriptBlockRegistered("bc_header_script"))
		Page.RegisterClientScriptBlock("bc_header_script", "<script type=\"text/javascript\" src=\"scripts/utils.js\"></script>");

	string init_script = string.Format("<script type=\"text/javascript\">bc_load('{0}', '{1}');</script>",
		inputField.ClientID, list_files.ClientID);
	Page.RegisterClientScriptBlock("bc_loader", init_script);
	base.OnPreRender(e);
}

A continuación, ponemos los elementos iniciales para que el script pueda hacer su trabajo:

<div class="demo">
    <h2>Selecciona un archivo</h2>
    <p>
    <input id="inputField" type="file" runat="server" />
    <asp:Button ID="cmdUpload" runat="server" Text="Subir" />
    </p>
    <ul id="list_files" runat="server">
    </ul>
</div>

Finalmente, implementamos el método que se encargará de guardar los archivos subidos al servidor:

private void cmdUpload_Click(object sender, System.EventArgs e)
{
	HttpFileCollection files = HttpContext.Current.Request.Files;
	for (int i = 0; i < files.Count; i++)
	{
		HttpPostedFile file = files[i];

		if (file.ContentLength == 0)
			continue;
		try
		{
			//upload.SaveAs(Path.Combine(@"C:\Uploads", file.FileName));
			Response.Write(file.FileName);
		}
		catch
		{
			// Manejar el error
		}
	}
}

Nota 1: El método presentado aquí para subir archivos se basa en la funcionalidad que trae ASP.NET, el cuál está limitado por la cantidad de bytes definido en el atributo maxRequestLength de la sección httpRuntime, así como también el tiempo máximo de ejecución del script. Este método normalmente falla cuando se suben archivos de tamaño considerable (la página no carga), felizmente este problema se puede solucionar usando módulos HTTP (ver NeatUpload).

Nota 2: Lamentablemente no puedo poner un demo para que vean funcionando el script, porque el servidor de hosting no soporta .NET

Archivos Relacionados