Redimensionar y guardar imágenes en ASP.NET

Este artículo en particular es como respuesta a dos personas que me pidieron ayuda con un par de artículos que escribí hace tiempo: Subir archivos al servidor/base de datos y Redimensionar imágenes. En su momento, lo que escribí resolvía esos temas, bajo ciertas condiciones que probablemente no eran las mismas para todos.

Sin embargo, gracias a proyectos más recientes, descubrí que hay algunos errores en el código que utilicé en aquél entonces, y he ido mejorando. También hay cosas que se pueden hacer de mejor manera (como en todo), así que creé un proyecto sencillo que aplica lo de ambos artículos, con algunas muy importantes mejoras.

Sigo recomendando los artículos anteriores como base, y no voy a entrar en detalle en éste. Solo hablaré de dos puntos en especial: uno que fue el cambio más grande en el código respecto a los artículos anteriores, y otro que es de importante consideración, no solo para imágenes, sino para cualquier tipo de archivo.

El código de ejemplo está al final, si no quieren leer esto. Realmente recomiendo leerlo, en primera… pues… porque ya lo escribí… y en segunda, porque no es únicamente para lo que hablé en esos posts anteriores, y les puede servir para otros casos.

Tener cuidado con los objetos Stream

Originalmente, utilicé la clase System.Drawing.Image para hacer toda la operación de redimensionamiento. Esto implica el uso de uno o varios objetos de tipo Sytem.IO.Stream, particularmente MemoryStream. El detalle con los objetos Stream es que, cuando abren un archivo, generalmente lo bloquean para uso exclusivo, por lo que se pueden presentar diferentes problemas durante el manejo del proyecto. Un objeto Stream guarda el contenido de un elemento (digamos un archivo), para poder leerlo, guardarlo, enviarlo a una variable, etc. La lectura de este objeto se basa en un puntero (su propiedad Position) que recorre el archivo, byte por byte, hasta su final. Cuando queremos volver a recorrer el contenido de un Stream ya «leído», hay que devolver ese puntero al principio. Cuando trabajamos con distintos Stream que tienen la misma base (por ejemplo, un BinaryStream basado en un FileStream), el mismo puntero aplica para todos, por lo que debemos cuidar que el puntero siempre esté en la posición deseada cuando usamos cada Stream.

El otro punto a considerar con la clase Stream es el «cerrar» los objetos. Es obligatorio ejecutar siempre las funciones Flush, Close y Dispose cuando dejamos de trabajar con un Stream.

Suponiendo que estamos mostrando las imágenes de una carpeta en nuestro sitio, usando un método que recurre a un Stream para leer la imagen y mostrarla en la página. Si el objeto Stream no fue cerrado, el sistema considerará que la imagen está abierta. El usuario podrá cerrar la página y desconectarse de nuestro sitio, pero la aplicación .NET seguirá «teniendo apartada» la imagen. Me pasó en una ocasión, y no encontré otra solución que detener el servidor virtual de Visual Studio.

El problema es ¿qué pasa con un Stream originado por otro? Si cierro un Stream, ¿qué pasa con los otros? ¿Se libera el archivo? ¿Tengo que cerrar todos? Por la premura del proyecto, y tomando en cuenta que ya eran demasiadas condiciones las que se presentan con el uso de los Stream, mejor opté por buscar otra solución.

Esta solución fue trabajar directamente con los arreglos de bytes. En lugar de estar pasando un Stream de un método a otro, los uso únicamente dentro de cada método, y los cierro cuando los desocupo. Si genero un Stream, extraigo los bytes a un arreglo, y este arreglo lo envío a otro método, independientemente si el método también utiliza un Stream, ya no será el mismo archivo lo que se cargue. El arreglo de bytes es una variable independiente, por lo que cualquier uso que le dé no afectará al Stream original. Con eso se resuelve mi problema de los archivos bloqueados.

Permisos en la carpeta donde se guardan las imágenes

ASP.NET requiere privilegios de lectura en la carpeta donde se va a ejecutar el sitio. Eso es de cajón. Pero cuando hablamos de «subir» archivos al servidor, necesitamos también permisos de escritura. El problema siempre está en saber a qué usuario se le deben dar los permisos de escritura en la carpeta.

Visual Studio no solicita permisos durante el desarrollo del proyecto, pero al momento de montarlo en el servidor, ahí sí se deben definir los privilegios de usuarios. Descubrí que es son algo distintas las condiciones entre ASP.NET 2.0 y ASP.NET 4.0 (no sé con 4.5 pero no me sorprendería si también es diferente). Pero descubrí un «truco» que puede resolver este problema de la forma, pienso yo, más automatizada.

En el ejemplo, estoy usando una carpeta temp en la raíz de la aplicación. Mi recomendación es no crear la carpeta en el servidor. En el código, donde se utiliza esa carpeta, estoy usando este código:

if (!Directory.Exists(ruta) {
  Directory.CreateDirectory(ruta);
}

¿Cuál es la diferencia entre hacer esta operación y crear a mano la carpeta? Que la carpeta la creamos con nuestra cuenta de usuario en el servidor, y siendo nuestra cuenta la «creadora», es la que tiene permisos para modificarla. Pero la cuenta bajo la que se ejecuta la aplicación en IIS no es la misma con la que iniciamos sesión (ni debe serlo). En cambio, cuando la operación se hace desde el código de la aplicación, es precisamente la cuenta usada por IIS la que la está creando, y por lo tanto, es la cuenta que puede crear, borrar o modificar archivos dentro de ella. No hace falta buscar permisos especiales para modificarla. El trabajo ya está hecho, y solo bastó una instrucción desde nuestra aplicación.

 

Teniendo en cuenta esto, más los artículos anteriores, aquí les dejo el código. Lo hice lo más sencillo posible, y sí, es en gran parte por todo el trabajo que tengo últimamente. Pero lo documenté lo mejor posible.

https://github.com/israel-munoz/dotnet-aspnet-resizeuploadimages

Una cosa más: si copian y pegan así nada más el código, no se sorprendan si no hace lo que necesitan. Cada proyecto puede tener sus propias particularidades, así que lo mejor es que revisen que el código solucione su problema y hagan los ajustes necesarios.

15 comentarios sobre “Redimensionar y guardar imágenes en ASP.NET

  1. gracias, por rasones de la escuela siempre guardaba imajenes en la BD pero ahora necesito hacerlo en el FileSystem, por lo cual tu articulo me ha sido de gran ayuda.

  2. Hola espero no llegar demasiado tarde, pero leyendo tus aportes llegue al re-dimensionado de imágenes, pero lamentablemente el link esta borrado, seria posible que lo re-subieras, saludos cordiales.

Replica a JUAN PABLO Cancelar la respuesta