ASP.NET MVC: Previsualizar imágenes subidas (2)

Buenas! Donde dije digo, digo Diego… Sí, ya sé que dije que el segundo post sería como hacerlo con Ajax, pero bueno… la culpa es de twitter, concretamente de @pablonete con el que hemos empezado a hablar sobre si es posible evitar el guardar la imágen físicamente en el servidor. Hay un mecanismo obvio, que es usar la sesión (guardar el array de bytes que conforman la imágen en la sesión). Pero… hay otra? Pues sí: usar data urls!

Data urls

Lo que mucha gente no conoce es que el formato de URL permite incrustar datos que son interpretados por el navegador como si se los hubiese descargado externamente. P.ej. la siguiente url es válida:

data:image/jpeg;base64,xxxxxxx

donde xxxxxxx es la codificación en base64 de la imágen.

P.ej. eso es totalmente correcto:

<img src=”data:image/jpeg;base64,xxxxxx/>

Veamos como podríamos modificar nuestro proyecto anterior, para usar data urls en lugar de un fichero temporal en el servidor… Fijaos que eso sólo evita guardar el fichero, la imagen debe ser subida al servidor.

Las modificaciones son muy simples, por un lado primero vamos a modificar la acción del controlador, para que quede así:

[HttpPost]
public ActionResult SendImage(HttpPostedFileBase img, string base64, string contenttype)
{
if (base64 != null && contenttype != null && img==null)
{
// Aquí podríamos guardar la imagen (en base64 tenemos los datos)
return View("Step2");
}
var data = new byte[img.ContentLength];
img.InputStream.Read(data, 0, img.ContentLength);
var base64Data = Convert.ToBase64String(data);
ViewBag.ImageData = base64Data;
ViewBag.ImageContentType = img.ContentType;
return View("Index");
}

La acción recibe tres parámetros:

  1. img: El fichero seleccionado (contenido del <input type=”file”).
  2. base64: Codificación en base64 de la imagen que se está previsualizándo.
  3. contenttype: Content-type de la imagen que se está previsualizando.

Por supuesto base64 y contenttype sólo se envían si se está previsualizando una imagen. En caso contrario valen null.

Fijémonos lo que hace la acción, si base64 y contenttype valen null, o bien el usuario ha seleccionado una imagen nueva (img != null):

  1. Obtenemos los datos en base64 de la imagen enviada por el usuario (base64Data).
  2. Pasamos esos datos (junto con el content-type) a la vista, usando el ViewBag.

Y la vista que hace? Pues poca cosa:

<form enctype="multipart/form-data" method="post" action="@Url.Action("SendImage")">
<label for="img">Seleccionar imagen:</label>
<input type="file" name="img" /> <br />
<input type="submit" />

@if (ViewBag.ImageData != null)
{
<img src="data:@ViewBag.ImageContentType;base64,@ViewBag.ImageData" />
<input type="hidden" value="@ViewBag.ImageData" name="base64" />
<input type="hidden" value="@ViewBag.ImageContentType" name="contenttype" />
}
</form>

Tenemos el formulario con el input type=”submit”, y luego si en el ViewBag vienen los datos de la imagen que se debe previsualizar, genera 3 campos más:

  1. Una imagen, cuyo src es una data url (formato data:content-type;base64,[datos en base64])
  2. Dos campos hidden (para guardar el content-type y los datos para mandarlos de vuelta al servidor).

Y listos! Con eso tenemos la previsualización de las imágenes sin necesidad de generar fichero temporal alguno.

Finalmente, tres aclaraciones:

  1. Fijaos que la codificación en base64 se incrusta en la página (en este caso se incrusta dos veces, aunque con un poco de javascript podría incrustarse solo una), por lo que esto puede generar páginas muy grandes si la imagen ocupa mucho.
  2. Si no voy errado, los navegadores sólo están obligados a soportar URLs de hasta 1024 bytes. Todo lo que exceda de aquí… depende del navegador. Firefox p.ej. acepta URLs muy largas, pero recordad que Base64 genera fácilmente URLs no muy largas, sinó descomunalmente largas (si la imágen ocupa 100Ks, la URL en Base64 ocupará más de 100Ks). Así que para imágenes pequeñas igual tiene sentido, pero para imágenes largas, honestamente no creo que sea una buena solución.
  3. La mayoría de navegadores modernos soportan data urls (IE lo empezó a hacer con su versión 8).

En fin… bueno, como curiosidad no está mal, eh? 😉

Gracias a Pablo Nuñez por la divertida e interesante conversación en twitter!

Saludos!

PD: Enlace al post anterior de esa serie: http://geeks.ms/blogs/etomas/archive/2011/03/15/asp-net-mvc-previsualizar-im-225-genes-subidas-1.aspx

Un comentario sobre “ASP.NET MVC: Previsualizar imágenes subidas (2)”

Responder a anonymous Cancelar respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *