Plupload es un componente de carga de archivos para navegadores web que ofrece una interfaz amigable, muestra el progreso de la carga, permite la previsualización automática de imágenes y la carga en fragmentos, facilitando la transferencia simultánea de varios archivos. Utiliza diversos motores de carga como Flash, Silverlight, HTML5, Gears, BrowserPlus o la carga de archivos estándar.
Desarrollado por los creadores de TinyMCE, Plupload se integra fácilmente en sistemas de gestión de contanido y aplicaciones similares. Actualmente, se compone de una API central y un componente de cola de carga basado en jQuery, lo que permite su uso directo o la creación de implementaciones personalizadas. Plupload se distribuye bajo la licencia GPLv2.
Funcionalidades de Plupload:
| Característica | Flash | Gears | HTML 5 | Silverlight | BrowserPlus | HTML 4 |
|---|---|---|---|---|---|---|
| Carga en Fragmentos | √ | √ | √¹ | √ | √ | X |
| Arrastrar y Soltar | X | √ | √² | X | √ | X |
| Redimensionar PNG | √ | √ | √³ | √ | √ | X |
| Redimensionar JPEG | √ | √ | √³ | √ | √ | X |
| Filtrado de Tipos | √ | √ | √⁴ | √ | √ | X |
| Carga en Streaming | √ | √ | √ | √ | X | X |
| Carga Multipart | √ | √ | √⁵ | √ | √ | √ |
| Restricción Tamaño | √ | √ | √ | √ | √ | X |
| Progreso de Carga | √ | √ | √ | √ | √ | X |
| Cabeceras Personaliz. | √ | √ | √ | √ | X | X |
Notas sobre las funcionalidades:
- La carga en fragmentos está bien soportada en Chrome y en la mayoría de Firefox 4+.
- La funcionalidad de arrastrar y soltar archivos solo está disponible en Firefox y WebKit. Safari en Windows presenta problemas que requieren soluciones alternativas.
- El redimensionamiento de imágenes solo es posible en Firefox 3.5+ (con calidad fija) y Chrome. Safari/Opera no permiten acceso directo a los datos de los archivos seleccionados.
- El filtrado de tipos de archivo no es compatible con todos los navegadores. Sin embargo, se utiliza el atributo
acceptde HTML5, por lo que funcionará cuando los navegadores lo soporten. - La carga multipart solo es compatible con Gecko y WebKit.
Características Clave de Plupload:
- Utiliza el componente jQuery para la selección y gestión de la cola de archivos.
- Emplea diversos motores de carga: Flash, Silverlight, HTML5, Gears, BrowserPlus y carga de archivos estándar.
- Permite el uso de la API central de Plupload para personalizar la selección y carga de archivos.
- El diálogo de selección de archivos, activado por JavaScript, puede configurarse para permitir la selección de uno o múltiples archivos, y restringir los tipos de archivo permitidos (ej.
jpg,gif). - Ofrece la posibilidad de personalizar eventos durante el proceso de carga.
- La carga de archivos seleccionados es independiente de la página y el formulario, permitiendo un procesamiento más sencillo de cada archivo individualmente en el servidor. Para más detalles, visite el sitio oficial de Plupload: http://www.plupload.com/
Implementación de Carga Masiva con Plupload
A continuación, se presenta una solución para permitir a los usuarios seleccionar múltiples archivos y cargarlos al servidor utilizando el componente Plupload.
Interfaz de Usuario:
- Visualización Inicial: Muestra la interfaz lista para la carga.
- Selección de Múltiples Archivos: Permite al usuario elegir varios archivos a la vez.
- Progreso de Carga: Muestra el avance de la carga de cada archivo.
Código y Pasos Detallados
Paso 1: Preparación del Componente PluploadDescargue el componente Plupload y sus demos desde el sitio oficial: http://www.plupload.com/
Paso 2: Estructura de Directorios de la AplicaciónOrganice los archivos de su proyecto según la siguiente estructura:
- Estructura Principal:

- Scripts de Carga de Archivos: Incluya los archivos JavaScript necesarios.
- Directorio
jquery.ui.plupload: Contiene los archivos para la interfaz visual del componente.
**Paso 3: Configuración del Frontend (Página Web)**Cree la interfaz web para la interacción del usuario.
Archivo WebUploadFileTest2.aspx (Frontend):
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="WebUploadFileTest2.aspx.cs" Inherits="WebApplication1.WebUploadFileTest2" %>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>Carga Múltiple de Archivos</title>
<style type="text/css">@import url(Scripts/jquery.ui.plupload/css/jquery.ui.plupload.css);</style>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.23/jquery-ui.min.js"></script>
<link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.23/themes/base/jquery-ui.css"/>
<script type="text/javascript" src="http://bp.yahooapis.com/2.4.21/browserplus-min.js"></script>
<script type="text/javascript" src="Scripts/plupload.full.js"></script>
<script type="text/javascript" src="Scripts/jquery.ui.plupload/jquery.ui.plupload.js"></script>
<script type="text/javascript">
$(function () {
$("#uploader").plupload({
runtimes: 'gears,flash,silverlight,browserplus,html5', // Motores de carga a utilizar
url: 'uploadFiles.ashx', // Endpoint del servidor para la carga
max_file_size: '10mb', // Límite máximo de tamaño por archivo
chunk_size: '1mb', // Tamaño de cada fragmento de carga
unique_names: true, // Generar nombres de archivo únicos en el servidor
// Opciones para redimensionamiento de imágenes en cliente (si está habilitado)
// resize: { width: 320, height: 240, quality: 90 },
// Filtros para tipos de archivo permitidos
filters: [
{ title: "Archivos de Imagen", extensions: "jpg,gif,png" },
{ title: "Archivos Zip", extensions: "zip,rar,7z" }
],
// Configuración para el runtime Flash
flash_swf_url: 'Scripts/plupload.flash.swf',
// Configuración para el runtime Silverlight
silverlight_xap_url: 'Scripts/plupload.silverlight.xap'
});
// Validación del formulario en el cliente
$('form').submit(function (e) {
var uploader = $('#uploader').plupload('getUploader');
if (uploader.files.length > 0) {
uploader.bind('StateChanged', function () {
if (uploader.files.length === (uploader.total.uploaded + uploader.total.failed)) {
// Enviar el formulario una vez que todos los archivos han sido cargados o fallaron
$('form')[0].submit();
}
});
uploader.start();
} else {
alert('Debe cargar al menos un archivo.');
}
return false;
});
});
</script>
</head>
<body>
<form id="form1" runat="server">
<div>
<h2>Carga Múltiple de Archivos</h2>
</div>
<div id="uploader" style="width: 600px">
<p>Su navegador no soporta Flash, Silverlight, Gears, BrowserPlus o HTML5.</p>
</div>
</form>
</body>
</html>
Resultado de la Interfaz:
**Archivo uploadFiles.ashx (Back end - ASP.NET Handler):**Este archivo maneja las solicitudes de carga del lado del servidor.
using System;
using System.IO;
using System.Web;
public class uploadFiles : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
context.Response.ContentType = "text/plain";
HandleFileUpload(context);
}
public bool IsReusable
{
get { return false; }
}
private void HandleFileUpload(HttpContext context)
{
context.Response.CacheControl = "no-cache";
string uploadDirectory = FileHelper.GetUploadPath(); // Directorio base para las cargas
string dateSubDir = DateTime.Now.ToString("yy-MM-dd");
string fullUploadDir = Path.Combine(uploadDirectory, dateSubDir);
if (!Directory.Exists(fullUploadDir))
{
Directory.CreateDirectory(fullUploadDir);
}
try
{
int chunkIndex = Convert.ToInt32(context.Request["chunk"]); // Índice del fragmento actual
int totalChunks = Convert.ToInt32(context.Request["chunks"]); // Número total de fragmentos
string originalFileName = context.Request["name"]; // Nombre original del archivo
// Si el archivo no se divide en fragmentos (totalChunks == 1)
if (totalChunks == 1)
{
HttpPostedFile file = context.Request.Files[0]; // Obtener el archivo
if (file.ContentLength > 0)
{
string filePath = Path.Combine(fullUploadDir, originalFileName);
file.SaveAs(filePath);
}
}
else // El archivo se está cargando en fragmentos
{
string tempFilePath = SaveTemporaryChunk(context.Request.Files[0], originalFileName, chunkIndex);
// Si es el último fragmento, ensamblar el archivo completo
if (chunkIndex == totalChunks - 1)
{
string finalFilePath = Path.Combine(fullUploadDir, originalFileName);
FileInfo tempFile = new FileInfo(tempFilePath);
// Si ya existe un archivo con el mismo nombre, eliminarlo
if (File.Exists(finalFilePath))
{
File.Delete(finalFilePath);
}
tempFile.MoveTo(finalFilePath); // Mover el archivo temporal a la ubicación final
}
}
}
catch (Exception ex)
{
context.Response.StatusCode = 500; // Indicar error del servidor
context.Response.Write($"Error al procesar la carga: {ex.Message}");
}
}
/// <summary>
/// Guarda un fragmento temporal del archivo.
/// </summary>
private string SaveTemporaryChunk(HttpPostedFile uploadedFile, string originalFileName, int chunkNumber)
{
string tempDir = FileHelper.GetTempPath(); // Directorio temporal
string chunkFileName = $"{originalFileName}.part{chunkNumber}";
string tempFilePath = Path.Combine(tempDir, chunkFileName);
// Crear el archivo temporal si no existe o añadir el fragmento si ya existe
using (FileStream fs = new FileStream(tempFilePath, chunkNumber == 0 ? FileMode.Create : FileMode.Append))
{
if (uploadedFile.ContentLength > 0)
{
byte[] buffer = new byte[uploadedFile.ContentLength];
uploadedFile.InputStream.Read(buffer, 0, uploadedFile.ContentLength);
fs.Write(buffer, 0, buffer.Length);
}
}
// Si es el último fragmento, renombrar el archivo temporal para que coincida con el nombre original
if (Convert.ToInt32(HttpContext.Current.Request["chunks"]) - 1 == chunkNumber)
{
string reassembledFilePath = Path.Combine(tempDir, originalFileName);
if (File.Exists(reassembledFilePath))
{
File.Delete(reassembledFilePath);
}
File.Move(tempFilePath, reassembledFilePath);
return reassembledFilePath;
}
return tempFilePath;
}
}
// Asumiendo que FileHelper es una clase auxiliar definida en otro archivo
public static class FileHelper
{
public static string GetUploadPath()
{
string basePath = HttpContext.Current.Server.MapPath("~/");
string subDir = ConfigurationManager.AppSettings["uploaddir"] ?? "Uploads";
string uploadDir = Path.Combine(basePath, subDir);
CreateDirectoryIfNotExists(uploadDir);
return uploadDir;
}
public static string GetTempPath()
{
string basePath = HttpContext.Current.Server.MapPath("~/");
string subDir = ConfigurationManager.AppSettings["tempdir"] ?? "TempUploads";
string tempDir = Path.Combine(basePath, subDir);
CreateDirectoryIfNotExists(tempDir);
return tempDir;
}
private static void CreateDirectoryIfNotExists(string path)
{
if (!Directory.Exists(path))
{
Directory.CreateDirectory(path);
}
}
}
**Paso 4: Clase Auxiliar FileHelper**Esta clase proporciona utilidades para gestionar directorios de carga y temporales.
using System;
using System.IO;
using System.Web;
using System.Configuration; // Necesario para ConfigurationManager
public static class FileHelper
{
/// <summary>
/// Obtiene la ruta base para las cargas, basada en la configuración.
/// </summary>
public static string GetUploadPath()
{
string basePath = HttpContext.Current.Server.MapPath("~/");
// Lee el nombre del subdirectorio de uploads desde el web.config
string subDirName = ConfigurationManager.AppSettings["uploaddir"] ?? "UploadedFiles";
string fullPath = Path.Combine(basePath, subDirName);
CreateDirectoryIfNotExists(fullPath);
return fullPath;
}
/// <summary>
/// Obtiene la ruta para los archivos temporales, basada en la configuración.
/// </summary>
public static string GetTempPath()
{
string basePath = HttpContext.Current.Server.MapPath("~/");
// Lee el nombre del subdirectorio de temporales desde el web.config
string tempDirName = ConfigurationManager.AppSettings["tempdir"] ?? "TempFiles";
string fullPath = Path.Combine(basePath, tempDirName);
CreateDirectoryIfNotExists(fullPath);
return fullPath;
}
/// <summary>
/// Crea un directorio si no existe.
/// </summary>
private static void CreateDirectoryIfNotExists(string path)
{
if (!Directory.Exists(path))
{
Directory.CreateDirectory(path);
}
}
}
Asegúrese de tener configurados los appSettings en su web.config para uploaddir y tempdir si desea personalizar los nombres de los directorios.
Paso 5: Miniaturas de ImágenesSi la opción resize está activada en la configuración de Plupload, las imágenes se redimensionarán en el cliente antes de la carga.
- Con
resizeactivado:
- Con
resizecomentado: Si se comenta la línearesize: { width: 320, height: 240, quality: 90 },, Plupload no generará miniaturas en el cliente.
Paso 6: Localización al EspañolPara mostrar la interfaz en español, incluya el archivo de idioma correspondiente.
<!-- ... otros scripts y estilos ... -->
<script type="text/javascript" src="Scripts/plupload.full.js"></script>
<script type="text/javascript" src="Scripts/i18n/es.js"></script> <!-- Archivo de idioma español -->
<script type="text/javascript" src="Scripts/jquery.ui.plupload/jquery.ui.plupload.js"></script>
<!-- ... -->
Interfaz en Español:
**Mensajes de Error:**Los mensajes de error se mostrarán según la configuración del idioma. 
**Archivos de Ejemplo:**Se proporcionan dos archivos de ejemplo:
WebUploadFileTest2.aspx: Corresponde al código de ejemplo principal de este tutorial.WebUploadFileTest.aspx: Otro archivo de ejemplo para demostración.