none
Problema al transferir archivo a servidor Web

    Question

  •  

    Hola, estoy tratando de subir un archivo Sdf a un servidor web para que el mismo pueda ser descargado a un dispositivo movil, y ocurre que el archivo llega corrupto. A modo de prueba intente transferir un archivo Zip y se corrompe de la misma forma, mientras que si intento subir un archivo de texto el mismo se transfiere correctamente. Este es el codigo C# que utilizo:

     

    string IpAdress_FileName;

    string arch;

    IpAdress_FileName = "http://misitioweb.no-ip.org/" + "Archivo.sdf";

    try

    {

    HttpWebRequest request = (HttpWebRequest) WebRequest.Create(IpAdress_FileName);

    request.Method = "PUT";

    request.AllowWriteStreamBuffering = true;

    StreamWriter writer = new StreamWriter(request.GetRequestStream());

    StreamReader reader = new StreamReader("c:\\Archivo.sdf");

    arch = reader.ReadToEnd();

    writer.Write(arch);

    writer.Close();

    reader.Close();

    ((HttpWebResponse)request.GetResponse()).Close();

     

    Queria saber si para transferir archivos que no sean de texto hay que transferirlos de otra forma.

    Muchas gracias por la ayuda que me puedan brindar.

    Friday, December 26, 2008 6:52 PM

Answers

  • Efectivamente, estás usando un StreamReader y un StreamWriter, que solo sirven para archivos de texto, pero no para binarios (ya que convierten los bytes leídos en caracteres Unicode).

     

    Tendrías que leer el archivo a transmitir directamente mediante un FileStream (sin interponer un StreamReader). El FileStream tiene un método Read que te devolverá un array de bytes (en lugar del string que te devuelve el StreamReader). Ese array de bytes lo puedes transmitir directamente con la función Write del Stream que obtienes cuando llamas a GetRequestStream (eliminando, por tanto, el StreamWriter).

     

    De esta manera mueves directamente bytes del fichero al servidor, en lugar de convertir los bytes en un string y luego el string de vuelta en bytes, cosa que corrompe el archivo binario porque no todas las secuencias de bytes tienen una correspondencia unívoca con texto Unicode.

     

    Wednesday, December 31, 2008 12:25 PM
    Moderator

All replies

  • Efectivamente, estás usando un StreamReader y un StreamWriter, que solo sirven para archivos de texto, pero no para binarios (ya que convierten los bytes leídos en caracteres Unicode).

     

    Tendrías que leer el archivo a transmitir directamente mediante un FileStream (sin interponer un StreamReader). El FileStream tiene un método Read que te devolverá un array de bytes (en lugar del string que te devuelve el StreamReader). Ese array de bytes lo puedes transmitir directamente con la función Write del Stream que obtienes cuando llamas a GetRequestStream (eliminando, por tanto, el StreamWriter).

     

    De esta manera mueves directamente bytes del fichero al servidor, en lugar de convertir los bytes en un string y luego el string de vuelta en bytes, cosa que corrompe el archivo binario porque no todas las secuencias de bytes tienen una correspondencia unívoca con texto Unicode.

     

    Wednesday, December 31, 2008 12:25 PM
    Moderator
  • Como comenta Alberto, juntas caracteres con bytes, aunque en lenguajes de mas bajo nivel un char ocupa un byte, en c#, java y otros lenguajes unicamente OO, existe una diferencia. Esta diferencia se debe a la codificacion de caracteres para los diferentes paises, en los cuales un caracter puede usar 2 o mas bytes (de memoria).

    Una vez dicho esto, quiero comentar, que esto se puede hacer de varias formas y voy a poner una de las mas faciles:

    Mediante esta funcion se pueden subir archivos a un servidor con el objeto WebClient.

    /// <summary>Funcion para subir un documento al servidor desde una ruta como origen</summary>
    /// <param name="origen">Ruta origen donde reside el documento (con nombre de fichero incluido)</param>
    /// <param name="NombreDocumento">Nombre del documento</param>
    /// <param name="destino">ruta de destino en servidor donde sera guardado</param>
    public void CopiarDocumento(String origen, String NombreDocumento, String destino)
    {
        // inicialmente no hay errores
        error = "";
        // Solo descomentar para usar certificados mediante SSL
        //if (Certificado)
            //ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(ValidateServerCertificate);
    
        try
        {
            string RutaDocumento = origen;
    
            System.IO.FileStream objStream = new
                System.IO.FileStream(RutaDocumento, System.IO.FileMode.Open, System.IO.FileAccess.Read);
            System.IO.BinaryReader objReader = new System.IO.BinaryReader(objStream);
            byte[] arrFileBytes = objReader.ReadBytes((int)objStream.Length);
            objReader.Close();
            objStream.Close();
    
            string Destino = destino + NombreDocumento;
    
            System.Net.WebClient objWebClient = new System.Net.WebClient();
            objWebClient.Credentials = new NetworkCredential(ServerUser, ServerPass, ServerDomain);
            objWebClient.UploadData(Destino, "PUT", arrFileBytes);
        }
        catch (Exception err)
        {
            error = err.Message.ToString();
        }
    }
    


    y para obtener un archivo, es aun mas facil con el mismo objeto.

    /// <summary>Obtener un documento de un servidor en un array de bytes para usar como stream</summary>
    /// <param name="_url">URL del servidor con el nombre del recurso a obtener</param>
    /// <returns>Array de bytes con el archivo</returns>
    public Byte[] openDocument(string _url)
    {    
        System.Net.WebClient myWebClient = new System.Net.WebClient();
        myWebClient.Credentials = new NetworkCredential(ServerUser, ServerPass, ServerDomain);
        string remoteUri = _url;
    
        // descargar el recurso web de la url del servidor y guardarlo en un buffer de bytes
        return myWebClient.DownloadData(remoteUri);
        
    }
    



    El objeto WebClient ayuda mucho a la hora de realizar estas funciones genericas.


    Saludos
    David González
    Tuesday, July 14, 2009 7:25 AM
  • Perdona mi torpeza, pero que cmo usas el certificado

    if (Certificado)
            ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(ValidateServerCertificate);
    
    he intentado usar el codigo sin esa parte, y me lanza un error, "Error en el servidor remoto: (405) metodo no permitido" es por quitar eso o por otra cosa.

    ayuda please.

    Tuesday, August 25, 2009 11:44 AM
  • Hola avielo.

    En realidad, esa linea lo que hace es comprobar si se quiere usar o no un certificado de seguridad para la comunicacion con el servidor.

    En otras palabras, si tu servidor usa SSL tendras que hacer uso de esa linea, en caso contrario no hace falta.

    La variable Certificado es un 'bool' que indicara si se quiere usar o no certificado.

    una posible funcion callback para comprobar el certificado seria:

    public static bool ValidateServerCertificate(
          object sender,
          X509Certificate certificate,
          X509Chain chain,
          SslPolicyErrors sslPolicyErrors)
    {
       if (sslPolicyErrors == SslPolicyErrors.None)
            return true;
    
        // no permitir que el cliente se comunique con el servidor sin autenticar
        return false;
    }
    Mediante esta funcion callback comprobarias el certificado.

    En cualquier caso ya te digo que la utilidad es para usar conexion de socket seguro SSL. Si no lo tienes configurado en el servidor se puede quitar.

    El error que te da es posible que sea por no tener las credenciales de acceso correctas.

    Si el servidor requiere credenciales de acceso, se tienen que especificar con por ejemplo la siguiente linea (que debe ir antes del UploadData):

    objWebClient.Credentials = new NetworkCredential(this.ServerUser, this.ServerPass, this.ServerDomain);

    Si no se establecen credenciales, se usan credenciales por defecto, o sea, ninguna.

    Prueba con todo esto y ya me cuentas como te a ido.


    NOTA:He modificado el mensaje anterior con las credenciales y SSL comentado para no usar







    Saludos
    David González
    Wednesday, August 26, 2009 6:50 AM