Hola.
Recorriendo varios foros encontré este problema, voy a intentar ser lo más claro posible. El problema principal es que no hay muchos ejemplos y los que hay son poco claro.
Para abordar este tema hay que tener en cuenta las necesidades de cada aplicación y la configuración del servidor al que vamos a enviar el archivo
De la aplicación tener en cuenta
- Si se envía datos de cadena solamente
- Si se envía archivo/s
- Utiliza buffer de escritura
Del servidor tener en cuanta
- Que protocolo utiliza
- Si es a través de HTTPS (SSL) requiere que revisemos el certificado del servidor
- Que tipo de autenticación utiliza
- Anónima
- Integrada
- Debemos pasarle las credenciales de conexión o indicarle que utilice las por defecto
- Básica
- Indicarle en el head del post el usuario y la contraseña
Certificado del servidor
Cuando la conexión se realiza a través de HTTPS el objeto HttpWebRequest va a requerir una función que valide el certicado del servidor.
¿Como se hace?
1) Crear la función que analiza el certificado
Código vb:
Ver originalPrivate Function ValidateCertificate(ByVal sender As Object, ByVal certificate As X509Certificate, ByVal chain As X509Chain, ByVal sslPolicyErrors As SslPolicyErrors) As Boolean
Dim resultado As Boolean
resultado = True
'
' Analizar el certificado y devolver true o false según sea el caso
'
Return resultado
End Function
2) Asignar la misma al “ServicePointManager”
Código vb:
Ver originalServicePointManager.ServerCertificateValidationCallback = New RemoteCertificateValidationCallback(AddressOf ValidateCertificate)
Autenticación
Según el tipo de autenticación requerida por el servidor utilizaremos alguno de los siguiente códigos, hay otras formas de asignar las credenciales, les dejo las que me parecen las más simples
Autenticación Anónima
Podemos obviar el codigo o indicarle que utilizaremos las credenciales por defecto
Código vb:
Ver originalHttpWRequest.Credentials = CredentialCache.DefaultCredentials
Autenticación integrada
Debemos darle las credenciales apropiadas
Código vb:
Ver originalDim creds As New Net.NetworkCredential(USERNAME, PSSWD, Domain)
HttpWRequest.Credentials = creds
Buffer de escritura
Otro tema a tener en cuenta es si la escritura se realiza directamente al flujo de salida o se retiene en buffer para luego ser enviado.
Para la mayoría de las aplicaciones es más cómodo utilizar el buffer ya que nos permite manejar de forma más simple los errores, la escritura directa se utiliza para envíos de stream, para enviar archivos de gran tamaño con control de progreso en el envío, etc.
Código vb:
Ver originalHttpWRequest.AllowWriteStreamBuffering = True
Enviar datos
Ahora si, pasado el certificado y la autenticación ahora vamos a enviar los datos.
Primero tener en cuenta que si se van a enviar archivos se debe utilizar el contentType=”multipart/form-data” que es distinto de cuando se envía solamente cadenas (contentType=”application/x-www-form-urlencoded” )
Enviar solamente cadenas
Cuando se requiere enviar datos de cadena hay que generar una cadena igual a la del metodo “GET“, que en lugar de concatenarla a la URL se envía por “POST”.
Vi algunas implementaciones donde envían archivos como cadenas codificados en base64, si bien en principio esto funciona no lo recomiendo ya que para eso esta el otro enctype (multipart/form-data)
Código vb:
Ver originalHttpWRequest.Method = "POST"
HttpWRequest.ContentType = "application/x-www-form-urlencoded"
'Generar cadena igual que para el metodo get
Dim data As StringBuilder = New StringBuilder()
data.Append("param01=" + HttpUtility.UrlEncode("valor01"))
data.Append("¶m02=" + HttpUtility.UrlEncode("valor02"))
data.Append("¶m03=" + HttpUtility.UrlEncode("valor03"))
'Crear array de bytes de la cadena
Dim byteData() As Byte
byteData = UTF8Encoding.UTF8.GetBytes(data.ToString())
'Setear el largo del stream
HttpWRequest.ContentLength = byteData.Length
'Escribir datos
Dim postStream As Stream = Nothing
postStream = wr.GetRequestStream()
postStream.Write(byteData, 0, byteData.Length)
'Enviar los datos (si buffer está activado) y recuperar la respuesta
Dim Response As HttpWebResponse
Response = wr.GetResponse()
Enviar archivos
Para enviar archivo se debe utilizar el contentType “multipart/form-data” del método “POST” esta codificación es una estructura de datos que permite incluir en la misma el contenido de los archivo. Es la que utilizan los exploradores para enviar archivos cuando se crean los forms con input de tipo file.
Código HTML:
Ver original<form name="formDocs" method="post" action="pagina.asp" enctype="multipart/form-data"> <input type="hidden" name="param01" value="valor01"/> <input type="file" name=" archivo" />
Este el el código
Código vb:
Ver originalDim LocalFile As String = "c:\archivo_a_enviar.txt"
HttpWRequest.Method = "POST"
'Hay que generar un limite y asegurarnos que sea único
'Por ejemplo
Dim limite As String = "----------" & DateTime.Now.Ticks.ToString("x")
HttpWRequest.ContentType = "multipart/form-data; boundary=" & limite
Dim sb As StringBuilder = New StringBuilder()
'Por cada parametro hay que generar una estructura que varía
'si es de archivo o no
'Parámnetro de cadena
sb.Append("--")
sb.Append(limite)
sb.Append(vbNewLine)
sb.Append("Content-Disposition: form-data; name=""")
sb.Append("param01")
sb.Append("""")
sb.Append(vbNewLine)
sb.Append(vbNewLine)
sb.Append("param_valor01")
sb.Append(vbNewLine)
'Parámnetro de archivo
sb.Append("--")
sb.Append(limite)
sb.Append(vbNewLine)
sb.Append("Content-Disposition: form-data; name=""")
sb.Append("sampfile")
sb.Append("""; filename=""")
sb.Append(Path.GetFileName(LocalFile))
sb.Append("""")
sb.Append(vbNewLine)
sb.Append("Content-Type: ")
sb.Append("application/octet-stream")
sb.Append(vbNewLine)
sb.Append(vbNewLine)
'pasamos la cadena a matriz de bytes
Dim postHeader As String = sb.ToString()
Dim postHeaderBytes As Byte() = Encoding.UTF8.GetBytes(postHeader)
'Crear la string de límite final como matriz de bytes
Dim limiteBytes As Byte() = Encoding.UTF8.GetBytes(vbNewLine & "--" + limite + vbNewLine)
'Crear un stream con el archivo
Dim fileStream As FileStream = New FileStream(LocalFile, FileMode.Open, FileAccess.Read)
'Calcular el largo del contenido
Dim length As Long = postHeaderBytes.Length + fileStream.Length + limiteBytes.Length
HttpWRequest.ContentLength = length
Dim stream As Stream = wr.GetRequestStream()
'Escribir cabecera
stream.Write(postHeaderBytes, 0, postHeaderBytes.Length)
'Escribir contenido del archivo
Dim buffer As Byte() = {}
ReDim buffer(fileStream.Length - 1)
Dim bytesRead As Integer = 0
bytesRead = fileStream.Read(buffer, 0, buffer.Length)
While bytesRead <> 0
stream.Write(buffer, 0, bytesRead)
bytesRead = fileStream.Read(buffer, 0, buffer.Length)
End While
'Escribir el límite final
stream.Write(limiteBytes, 0, limiteBytes.Length)
'Enviar los datos (si buffer está activado) y recuperar la respuesta
Dim Response As HttpWebResponse
Response = wr.GetResponse()