none
Excepción al hacer Image.FromStream para obtener imagen desde la base de datos RRS feed

  • Pregunta

  • Buenas nuevamente por aquí en este reto con las imagenes.

    Bien, luego de conversar largo y tendido con mi asesor de proyecto, me dio permiso para cambiar el modo de trabajo que hago ahora con las imagenes, y de poder guardarlas en la base de datos como imagenes y no la ruta.

    Tomando como referencia el link que me propuso sh4, logré hacer que mi sistema guarde las imagenes correctamente en la base de datos, pero no al revés.

    En un formulario que muestra una imagen grande del producto seleccionado de una grilla, en el metodo Load para que cargue la imagen hago lo siguiente:

    byte[] imagenDatos = (byte[])objetoProducto.Imagen; //Imagen es la propiedad del objeto de la entidad Producto

    Image imagen;

    using (MemoryStream ms = new MemoryStream(imagenDatos , 0, imagenDatos .Length))

    {

    ms.Write(imagenDatos , 0, imagenDatos .Length);

    imagen= Image.FromStream(ms, true);

    }

    pictureBox1.Image = imagen;


    Todo lo carga y hace bien hasta que llega a la linea imagen= Image.FromStream(ms, true); , que lanza "No se controló ArgumentException: El parámetro no es válido".

    No se por que me pasa en mi sistema ya que en el que bajé de esa página de prueba no lanza error al pasar por esa línea, no se qué cosa está mal.

    Agradezco de nuevo su ayuda, y perdón las molestias!

    Marcos

    jueves, 5 de marzo de 2009 3:19

Respuestas

  • No se como lo hayas hecho...

    sigue mis recomendaciones:

    para convertirlo a byte[]

                Image i = Image.FromFile("osito.jpg"); 
     
                MemoryStream m = new MemoryStream(); 
     
                i.Save(m, ImageFormat.Bmp); 
                byte[] imagenDatos = m.ToArray(); 
     
                m.Close(); 


    Para, desde el byte[] volverlo imagen:

                MemoryStream ms = new MemoryStream(imagenDatos); 
                imagenNueva = Image.FromStream(ms); 
                ms.Close(); 

    Desde luego seria conveniente manejarlo en jpg para ahorrar espacio.


    Juan Carlos Ruiz - http://juank.black-byte.com


    viernes, 6 de marzo de 2009 14:04
    Moderador
  • oops,
    ya revisando lo que me mandaste mas a fondo...

    porque guardas las imagenes en un campo varchar?? y de 300!!!

    Esto no deberia ser así, primero un campo varchar de 300 esta diseñado para almacenar seguramente 600 bytes pues cada char debe ser un caracter de 16 bits... o sea dos bytes...

    de que tamaño son las imagenes ? porque usas varchar?


    mi recomendacion:

    No uses campos tipo varchar para guardar las imagenes , y mucho menos un campo que tenga restriccion de tamaño, ese campo en la tabla debe ser de tipo LOB o BLOB, especificamente BLOB (binary large object).

    No soy experto en SQL Server, pero creo que debes utilizar un campo tipo VARBINARY(MAX)

    Revisa este link>

    http://www.guillesql.es/Articulos/SQLServerLOBs.aspx



    Juan Carlos Ruiz - http://juank.black-byte.com
    domingo, 8 de marzo de 2009 23:16
    Moderador
  • MarcosGN dijo:

    Ha funcionado!!!!!!!!!!!!!!! muchísssssssssimas gracias!!!!!!!!!!!

    Lo tonto fue que en la tabla el tipo de datos era image pero en el procedimiento era varchar de 300, menuda tontería de esas que complican la vida de uno!

    Con esto me di cuenta de que en mis registros de prueba, los que ingresé yo a mano en la base de datos, el código que tiene el campo de imagen es erróneo entonces cuando trato de ver la imagen de uno de esos productos ingresados a mano en la base de datos da el mismo error, y funciona solo si doy de alta un producto desde mi sistema.

    Esos registros los necesito ya que debo dejar la base con datos de prueba, lo que hago es lo siguiente:
    INSERT INTO Productos(nombre, precio, imagen) VALUES ('Bolso', 50, 'C:/Imagenes/Productos/Bolso.jpg');
    yo me pensaba que asi se guardaba la imagen, pero veo que no, será la dirección de memoria.
    Hace unos días pregunté en el foro de Sql Server, pero no me ha resultado :(
    http://social.msdn.microsoft.com/Forums/es-ES/sqlserveres/thread/cccb4fc7-3ce0-4534-8da9-83a057081534

    Supuestamente si en un DataGridView tengo una columna del tipo DataGridViewImageColumn, al asignarle el source debería mostrar la imagen verdad? Leí que ese campo trabaja con bytes o Image, pero no se si hay que convertirlo porque me muestra una cruz roja, quizás sea por el tema ese de que están mal guardadas las imagenes que puse a mano?

    Mil gracias de nuevo!!!!!!!

    Hola:

    Para insertar fotos, debes primero convertirlo a byte[] (es lo más natural) (por ejemplo, como te comenta arriba Juan Carlos) y después insertar el campo byte a la columna image de la base de datos.

    Yo utilizo el siguiente procedimiento almacenado, tengo animales con su ID y su foto.

    CREATE  PROCEDURE [dbo].Foto_Insertar 
        @animal char(6) 
        ,@foto image 
    AS 
     
    INSERT INTO [dbo].[Foto] ( 
        [animal] 
        ,[foto] 
    ) VALUES ( 
        @animal 
        ,@foto 
    )  

    Utilizo procedimiento almacenado para cada tarea de mantenimiento (insertar, actualizar, eliminar) que hacen exactamente lo mismo, pero con una insert, una update y una delete, y llamo a cada uno de ellos cuando proceda.

    Te expongo la función con la que llamo al procedimiento superior:

    foto es de tipo image y animal mi ID Principal, que es un string.

    byte[] en C# == image en SQL Server en este caso

    private void Insertar() 
            { 
                if(Existe(animal)) 
                    throw new Exception("El registro que esta intentando introducir ya existe"); 
                     
                SqlCommand  cmdToExecute = new SqlCommand(); 
                cmdToExecute.CommandText = "dbo.[Foto_Insertar]"
                cmdToExecute.CommandType = CommandType.StoredProcedure; 
     
                cmdToExecute.Connection = this.Conexion; 
     
                try 
                { 
                    cmdToExecute.Parameters.Add(new SqlParameter("@animal", animal)); 
                    cmdToExecute.Parameters.Add(new SqlParameter("@foto", foto)); 
     
                    if(this.Conexion.State!=ConnectionState.Open) 
                        this.Conexion.Open(); 
                    else 
                        cmdToExecute.Transaction=this.Transaccion; 
     
                    cmdToExecute.ExecuteNonQuery(); 
                    nuevo=false
                } 
                catch(Exception ex) 
                { 
                    throw new Exception(ex.Message, ex); 
                } 
                finally 
                { 
                    if(this.Transaccion==null) 
                        this.Conexion.Close(); 
                    cmdToExecute.Dispose(); 
                } 
                 
            } 

    Las funciones de Actualizar, Eliminar son iguales, pero llamando a su procedimiento almacenado correspondiente.

    Se puede automatizar todo mucho, yo utilizo casi siempre la misma estructura.

    Por ejemplo, la función Existe(), que llama a un procedimiento almacenado y busca la "id" enviada para saber si el registro existe, aunque parezca una nimiez, es muy útil.

    Espero que esto te ayude.

    Saludos.
    lunes, 9 de marzo de 2009 8:04
  • Creo que no leiste el link que te pase... o al menos no muy a  fondo.

    http://www.guillesql.es/Articulos/SQLServerLOBs.aspx

    alli esta todo lo que necesitas,
    que en resumen es:

    1. La base de datos debe tener una columna tipo image o cualquiera capaz de guardar datos binarios es decir BLOB, como lo puede ser tambien VARBINARY etc.
    2. Al insertar , actualizar información en la base de datos donde se afecte dicha columna, debes utilizar las funciones de ADO indicadas para cargar las imágenes o archivos. Por ejemplo si la columna en la base de datos es de tipo image y en tu programa C#  byteImage es un byte[] con la imagen entonces para el insert haces haces:

    string sql = "insert into tabla(Campo1, Imagen)"
    sql += " Values(@Campo1, @Imagen)"
     


    SqlCom.Parameters.Add("@Campo1", System.Data.SqlDbType.VarChar, 150); 
    SqlCom.Parameters["@Campo1"].Value = textoCampo1; 
    SqlCom.Parameters.Add("@Imagen", System.Data.SqlDbType.Image); 
    SqlCom.Parameters["@Imagen"].Value = byteImage




    Juan Carlos Ruiz - http://juank.black-byte.com
    lunes, 9 de marzo de 2009 13:30
    Moderador

Todas las respuestas

  • creo que nte sobra una linea... dejalo asi:

    Image imagen;  
     
    using (MemoryStream ms = new MemoryStream(imagenDatos , 0, imagenDatos .Length))  
    {  
      imagen= Image.FromStream(ms, true);  
    }  
    pictureBox1.Image = imagen;  
     

    Juan Carlos Ruiz - http://juank.black-byte.com
    viernes, 6 de marzo de 2009 3:36
    Moderador
  • No ha funcionando quitando esa línea :( ya no sé qué hacer :(

    Gracias por responder
    viernes, 6 de marzo de 2009 6:16
  • No se como lo hayas hecho...

    sigue mis recomendaciones:

    para convertirlo a byte[]

                Image i = Image.FromFile("osito.jpg"); 
     
                MemoryStream m = new MemoryStream(); 
     
                i.Save(m, ImageFormat.Bmp); 
                byte[] imagenDatos = m.ToArray(); 
     
                m.Close(); 


    Para, desde el byte[] volverlo imagen:

                MemoryStream ms = new MemoryStream(imagenDatos); 
                imagenNueva = Image.FromStream(ms); 
                ms.Close(); 

    Desde luego seria conveniente manejarlo en jpg para ahorrar espacio.


    Juan Carlos Ruiz - http://juank.black-byte.com


    viernes, 6 de marzo de 2009 14:04
    Moderador
  • Juan Carlos:

    he probado lo que me dijiste:

    byte[] imagenDatos = (byte[])objetoProducto.Imagen;

    //Initialize image variable

    Image imagen;

    //Read image data into a memory stream

    MemoryStream ms = new MemoryStream(imagenDatos);

    imagen = Image.FromStream(ms);

    ms.Close();

    Cuando pasa por la misma línea que antes imagen = Image.FromStream(ms);  salta la misma excepción esto se está convirtiendo mi pesadilla ! :(

    Gracias por la ayuda nuevamente

    viernes, 6 de marzo de 2009 15:43
  • Claro, por eso te envie el codigo para convertir la imagen original a bytes porque es alli donde estas fallando
    Juan Carlos Ruiz - http://juank.black-byte.com
    viernes, 6 de marzo de 2009 16:01
    Moderador
  • Juan Carlos,

    he cambiado el código para guardar la imagen en la base de datos y ha funcionado, pero para sacarla y mostrarla en la pictureBox me sigue dando el error.

    Yo en la clase donde defino las propiedades del objeto Producto, la imagen la tengo como object, de la siguiente manera:

    private object _imagen;
    public object Imagen

    { get { return _imagen; }
    set { _imagen = value; } }

    Será que me da el error por ser de tipo object?

    viernes, 6 de marzo de 2009 22:31
  • que código usas para leer la imagen la primera vez antes de llevarla a la base de datos...
    debes tener algo mal a parte de lo que me has dicho pero necesitaría ver mas código.

    Juan Carlos Ruiz - http://juank.black-byte.com
    viernes, 6 de marzo de 2009 22:34
    Moderador
  • Cuando doy de alta un producto, en el botón Agregar, hago:

    Image i = Image.FromFile(openFileDialog1.FileName);
    MemoryStream m = new MemoryStream();
    i.Save(m,
    ImageFormat.Jpeg);
    byte[] oImagen = m.ToArray();
    m.Close();

    Y luego cuando armo el objeto
    Productos miProducto = new Productos(.......,......,....., oImagen, ......., ....) etc, ahí le paso el oImagen.
    Esto va por las distintas capas del sistema hasta que llega a Datos, donde se ejecuta el procedimiento almacenado que tengo en la base de datos, le pasa todos los parámetros y se hace el insert en la tabla.
    Hasta ahí todo parece correcto porque el producto se ingresa a la base de datos correctamente y veo que en el campo imagen está el código binario.

    Luego cuando voy a colocar la imagen de algún producto en la PictureBox es cuando da ese problema, por eso te preguntaba si algo tendría que ver que en la entidad Productos yo pusiera la propiedad de imagen como de tipo object.

    Si necesitas saber algun dato mas de cómo tengo hecho me preguntas,
    muchas gracias por tu atención!!

    viernes, 6 de marzo de 2009 23:10
  • si oImagen, que es un byte[] lo estas metiendo en Productos.Imagen que es object... ahi esta el problema...

    a la base de datos debes llevar el  propio byte[].

    Juan Carlos Ruiz - http://juank.black-byte.com
    viernes, 6 de marzo de 2009 23:25
    Moderador
  • Cambié en todo el programa el object de la imagen a byte[], guarda bien en la base de datos pero a la vuelta sigue pasando lo mismo.

    Hago debug para ver lo que va cargando, y cuando pasa por la línea 

    byte[] imagenDatos = (byte[])objetoProducto.Imagen; si el producto lo agregué por medio del programa, siempre dice { Dimensiones: [300] } , en cambio cuando quiero obtener la imagen de algún producto que ya está en la base de datos ingresado por medio del script de base de datos tiene otros valores.. es correcto?
    y ya de nuevo error en oImagen = Image.FromStream(ms); "el parámetro no es válido" :'(

    sábado, 7 de marzo de 2009 2:11
  • dificil , la diferencia de tamaños demuestra que algo estas haciedno mal, pero dadas las caracteristicas de tu proyecto es dificil revisarlo de esta manera.


    Si me peudes pasar el proyecto de algu modo o facilitar el macceso remoto a tu maquina para que lo revisemos seria de ayuda.

    De igual forma si me peudes pasar todos los fragmentos de codigo involucrados en este caso tal ves podamos hacer algo.
    Juan Carlos Ruiz - http://juank.black-byte.com
    sábado, 7 de marzo de 2009 3:53
    Moderador
  •  
    Cómo te comunico? antes en el perfil podía ver los correos, pero ahora con el cambio ya no.
    No se si se permite pasar el correo por aqui?
    sábado, 7 de marzo de 2009 6:23
  •  juankpuntoruizarrobagmail.com
    Juan Carlos Ruiz - http://juank.black-byte.com
    sábado, 7 de marzo de 2009 13:56
    Moderador
  • oops,
    ya revisando lo que me mandaste mas a fondo...

    porque guardas las imagenes en un campo varchar?? y de 300!!!

    Esto no deberia ser así, primero un campo varchar de 300 esta diseñado para almacenar seguramente 600 bytes pues cada char debe ser un caracter de 16 bits... o sea dos bytes...

    de que tamaño son las imagenes ? porque usas varchar?


    mi recomendacion:

    No uses campos tipo varchar para guardar las imagenes , y mucho menos un campo que tenga restriccion de tamaño, ese campo en la tabla debe ser de tipo LOB o BLOB, especificamente BLOB (binary large object).

    No soy experto en SQL Server, pero creo que debes utilizar un campo tipo VARBINARY(MAX)

    Revisa este link>

    http://www.guillesql.es/Articulos/SQLServerLOBs.aspx



    Juan Carlos Ruiz - http://juank.black-byte.com
    domingo, 8 de marzo de 2009 23:16
    Moderador
  • Ha funcionado!!!!!!!!!!!!!!! muchísssssssssimas gracias!!!!!!!!!!!

    Lo tonto fue que en la tabla el tipo de datos era image pero en el procedimiento era varchar de 300, menuda tontería de esas que complican la vida de uno!

    Con esto me di cuenta de que en mis registros de prueba, los que ingresé yo a mano en la base de datos, el código que tiene el campo de imagen es erróneo entonces cuando trato de ver la imagen de uno de esos productos ingresados a mano en la base de datos da el mismo error, y funciona solo si doy de alta un producto desde mi sistema.

    Esos registros los necesito ya que debo dejar la base con datos de prueba, lo que hago es lo siguiente:
    INSERT INTO Productos(nombre, precio, imagen) VALUES ('Bolso', 50, 'C:/Imagenes/Productos/Bolso.jpg');
    yo me pensaba que asi se guardaba la imagen, pero veo que no, será la dirección de memoria.
    Hace unos días pregunté en el foro de Sql Server, pero no me ha resultado :(
    http://social.msdn.microsoft.com/Forums/es-ES/sqlserveres/thread/cccb4fc7-3ce0-4534-8da9-83a057081534

    Supuestamente si en un DataGridView tengo una columna del tipo DataGridViewImageColumn, al asignarle el source debería mostrar la imagen verdad? Leí que ese campo trabaja con bytes o Image, pero no se si hay que convertirlo porque me muestra una cruz roja, quizás sea por el tema ese de que están mal guardadas las imagenes que puse a mano?

    Mil gracias de nuevo!!!!!!!
    lunes, 9 de marzo de 2009 3:27
  • MarcosGN dijo:

    Ha funcionado!!!!!!!!!!!!!!! muchísssssssssimas gracias!!!!!!!!!!!

    Lo tonto fue que en la tabla el tipo de datos era image pero en el procedimiento era varchar de 300, menuda tontería de esas que complican la vida de uno!

    Con esto me di cuenta de que en mis registros de prueba, los que ingresé yo a mano en la base de datos, el código que tiene el campo de imagen es erróneo entonces cuando trato de ver la imagen de uno de esos productos ingresados a mano en la base de datos da el mismo error, y funciona solo si doy de alta un producto desde mi sistema.

    Esos registros los necesito ya que debo dejar la base con datos de prueba, lo que hago es lo siguiente:
    INSERT INTO Productos(nombre, precio, imagen) VALUES ('Bolso', 50, 'C:/Imagenes/Productos/Bolso.jpg');
    yo me pensaba que asi se guardaba la imagen, pero veo que no, será la dirección de memoria.
    Hace unos días pregunté en el foro de Sql Server, pero no me ha resultado :(
    http://social.msdn.microsoft.com/Forums/es-ES/sqlserveres/thread/cccb4fc7-3ce0-4534-8da9-83a057081534

    Supuestamente si en un DataGridView tengo una columna del tipo DataGridViewImageColumn, al asignarle el source debería mostrar la imagen verdad? Leí que ese campo trabaja con bytes o Image, pero no se si hay que convertirlo porque me muestra una cruz roja, quizás sea por el tema ese de que están mal guardadas las imagenes que puse a mano?

    Mil gracias de nuevo!!!!!!!

    Hola:

    Para insertar fotos, debes primero convertirlo a byte[] (es lo más natural) (por ejemplo, como te comenta arriba Juan Carlos) y después insertar el campo byte a la columna image de la base de datos.

    Yo utilizo el siguiente procedimiento almacenado, tengo animales con su ID y su foto.

    CREATE  PROCEDURE [dbo].Foto_Insertar 
        @animal char(6) 
        ,@foto image 
    AS 
     
    INSERT INTO [dbo].[Foto] ( 
        [animal] 
        ,[foto] 
    ) VALUES ( 
        @animal 
        ,@foto 
    )  

    Utilizo procedimiento almacenado para cada tarea de mantenimiento (insertar, actualizar, eliminar) que hacen exactamente lo mismo, pero con una insert, una update y una delete, y llamo a cada uno de ellos cuando proceda.

    Te expongo la función con la que llamo al procedimiento superior:

    foto es de tipo image y animal mi ID Principal, que es un string.

    byte[] en C# == image en SQL Server en este caso

    private void Insertar() 
            { 
                if(Existe(animal)) 
                    throw new Exception("El registro que esta intentando introducir ya existe"); 
                     
                SqlCommand  cmdToExecute = new SqlCommand(); 
                cmdToExecute.CommandText = "dbo.[Foto_Insertar]"
                cmdToExecute.CommandType = CommandType.StoredProcedure; 
     
                cmdToExecute.Connection = this.Conexion; 
     
                try 
                { 
                    cmdToExecute.Parameters.Add(new SqlParameter("@animal", animal)); 
                    cmdToExecute.Parameters.Add(new SqlParameter("@foto", foto)); 
     
                    if(this.Conexion.State!=ConnectionState.Open) 
                        this.Conexion.Open(); 
                    else 
                        cmdToExecute.Transaction=this.Transaccion; 
     
                    cmdToExecute.ExecuteNonQuery(); 
                    nuevo=false
                } 
                catch(Exception ex) 
                { 
                    throw new Exception(ex.Message, ex); 
                } 
                finally 
                { 
                    if(this.Transaccion==null) 
                        this.Conexion.Close(); 
                    cmdToExecute.Dispose(); 
                } 
                 
            } 

    Las funciones de Actualizar, Eliminar son iguales, pero llamando a su procedimiento almacenado correspondiente.

    Se puede automatizar todo mucho, yo utilizo casi siempre la misma estructura.

    Por ejemplo, la función Existe(), que llama a un procedimiento almacenado y busca la "id" enviada para saber si el registro existe, aunque parezca una nimiez, es muy útil.

    Espero que esto te ayude.

    Saludos.
    lunes, 9 de marzo de 2009 8:04
  • Creo que no leiste el link que te pase... o al menos no muy a  fondo.

    http://www.guillesql.es/Articulos/SQLServerLOBs.aspx

    alli esta todo lo que necesitas,
    que en resumen es:

    1. La base de datos debe tener una columna tipo image o cualquiera capaz de guardar datos binarios es decir BLOB, como lo puede ser tambien VARBINARY etc.
    2. Al insertar , actualizar información en la base de datos donde se afecte dicha columna, debes utilizar las funciones de ADO indicadas para cargar las imágenes o archivos. Por ejemplo si la columna en la base de datos es de tipo image y en tu programa C#  byteImage es un byte[] con la imagen entonces para el insert haces haces:

    string sql = "insert into tabla(Campo1, Imagen)"
    sql += " Values(@Campo1, @Imagen)"
     


    SqlCom.Parameters.Add("@Campo1", System.Data.SqlDbType.VarChar, 150); 
    SqlCom.Parameters["@Campo1"].Value = textoCampo1; 
    SqlCom.Parameters.Add("@Imagen", System.Data.SqlDbType.Image); 
    SqlCom.Parameters["@Imagen"].Value = byteImage




    Juan Carlos Ruiz - http://juank.black-byte.com
    lunes, 9 de marzo de 2009 13:30
    Moderador
  • Juan Carlos, Sh4, muchas gracias por sus consejos y por la información brindada.

    Ya pude solucionar todo lo referente al tema imagenes (o al menos eso creo!! espero que no salte nada raro un día de estos).

    Gracias Juan Carlos por tu paciencia para con este novato!!!! :)

    Saludos cordiales,

    Marcos! :D

    martes, 10 de marzo de 2009 19:06
  • Con gusto!!!
    para eso estamos.!!!

    Juan Carlos Ruiz - http://juank.black-byte.com
    martes, 10 de marzo de 2009 19:26
    Moderador