|
Trouver une ressource
Vous ne trouvez pas de réponse à votre problème ? Alors posez la question dans le forum. Souvenez-vous qu'il n'y a jamais de question bête, mais rester dans l'ignorance parce que l'on n'ose pas poser une question, ça c'est une erreur !
ENVOI DE FICHIER PAR UNE REQUÊTE POST EN MULTIPART/FORM-DATA ("SIMLULATION" D'UN FORMULAIRE WEB CONTENANT UN FICHIER À ENVOYER)
Information sur la source
Description
Bonjour, J'ai longuement cherché la facon de faire pour envoyer une requête POST qui inclut le contenu d'un fichier (content-type: multipart/form-data), mais sans succès, et c'est pourquoi j'ai donc du le créer moi même :-) Le but est donc de simuler un formulaire d'une page web de ce type: <form method="POST" action="http://site.com" enctype="multipart/form-data"> <input type="hidden" name="MAX_FILE_SIZE" value="Taille_Octets"> <input type="file" name="fichier" size="30"><br> <input type="submit" name="telechargement" value="telecharger"> </form> Comment ca marche? J'utilise un HttpWebRequest pour créé la requête, puis j'y ajoute le contenu (que je forme avec une class que j'ai créé et que je vais partager ici) grâce à la méthode GetRequestStream du HttpWebRequest. Pour former le contenu, il faut savoir quel champ du formulaire doit être envoyé. Si vous voulez "simuler" une page web existante, je vous conseil de "sniffer" votre requête envoyé par la page web en question (avec Ethereal par exemple, un programme simple d'emplois et gratuit: http://www.ethereal.com/) Commencez la capture dans Ethereal puis envoyer votre fichier avec la page web. Une fois que c'est envoyé, arretez la capture et trouvez le flux TCP qui correspond à la communication qui vous intéresse (bouton droit et "follow TCP stream") Ca donne un truc de ce genre: POST / HTTP/1.1 User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; fr; rv:1.8.0.7) Gecko/20060909 Firefox/1.5.0.7 Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5 Accept-Language: fr,fr-fr;q=0.8,en-us;q=0.5,en;q=0.3 Accept-Encoding: gzip, deflate Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 Keep-Alive: 300 Referer: http://imageshack.us/ Content-Type: multipart/form-data; boundary=---------------------------41184676334 Host: imageshack.us Content-Length: 1517 Connection: Keep-Alive -----------------------------41184676334 Content-Disposition: form-data; name="uploadtype" on -----------------------------41184676334 Content-Disposition: form-data; name="fileupload"; filename="Sans titre.JPG" Content-Type: image/jpeg [..... Contenu du fichier .....] -----------------------------41184676334 Content-Disposition: form-data; name="MAX_FILE_SIZE" 3145728 -----------------------------41184676334 Content-Disposition: form-data; name="refer" http://site.com/ -----------------------------41184676334 Content-Disposition: form-data; name="brand" -----------------------------41184676334 Content-Disposition: form-data; name="optsize" 320x320 -----------------------------41184676334-- Ici par exemple, si il y a les champs suivants (leur nom) avec leur valeurs respectives: Champ "uploadtype", valeur "on" Champ "fileupload", ici il s'agit d'un champ FILE, il y a donc le paramètre "filename", le paramètre "Content-Type: image/jpeg", et la valeur = [Contenu du fichier]] Champ "MAX_FILE_SIZE", valeur "3145728" Champ "refer", valeur "http://site.com/" Champ "brand", valeur "" Champ "optsize", valeur "320x320" On ne sait pas de quel type sont le champ (TEXT, RADIO, CHECKBOX..) mais ce n'est pas important, sauf pour le champ FILE.
Source
- ' La requête se fait avec un HttpWebRequest, et on créé les données par la class que j'ai créé qui s'appel MultipartFormDataBodyCreator.
- '
- ' Propriété et procédures publiques de MultipartFormDataBodyCreator:
- '
- ' Public Sub Add(Name as String, _ ' Le nom du champs à ajouter
- ' Value as String, _ ' La valeur
- ' Optional IsFileField as Bollean = False") ' Si il s'agit d'un fichier, mettre IsFileField = true
- ' ' et Value = le chemin du fichier
- '
- ' Public Property Boundary as String ' Le Boundary à utiliser (sert à délimiter les champs dans la requête)
- '
- ' Public Function Create() as Byte() ' Retourne les données à envoyer sous forme d'un tableau de byte
- '
- '
- ' Pour envoyer la requête présenté juste au dessus, il faut donc faire comme ceci:
-
-
- Private Sub SendFile()
-
- Try
-
- Dim bodyCreator As New MultipartFormDataBodyCreater ' La classe que j'ai créé et que je présente plus loins
- Dim File as String = "C:\Image.jpg" ' Le fichier à envoyer
-
- ' Création des données à envoyer
- ' --------------------
- bodyCreator.Fields.Add("uploadtype", "on")
- bodyCreator.Fields.Add("fileupload", File, True) ' C'est le fichier
- bodyCreator.Fields.Add("MAX_FILE_SIZE", "3145728")
- bodyCreator.Fields.Add("refer", "http://site.com/")
- bodyCreator.Fields.Add("brand", "")
- bodyCreator.Fields.Add("optsize", "320x320")
-
- Dim body() as Byte = bodyCreator.Create ' Les données à envoyer sous forme de tableau de Byte
- Dim boundary as String = bodyCreater.Boundary ' Le boudary délimitant les champs. Utile pour créer la requête
-
- ' Création de la requête
- ' ------------------------
- Dim Request as HttpWebRequest = HttpWebRequest.Create("http://site.com")
- Request.Method = "POST"
- Request.UserAgent = "Mozilla/5.0 (Windows; U; Windows NT 5.1; fr; rv:1.8.0.7) Gecko/20060909 Firefox/1.5.0.7"
- Request.KeepAlive = True
- Request.ServicePoint.Expect100Continue = False ' Important si le serveur ne gère pas les Expect
- Request.Referer = "http://site.com/"
- Request.ContentType = "multipart/form-data; boundary=" & boundary ' Ajouter le boundary après le Content-Type
- Request.ContentLength = body.Length
-
- ' Ecriture des données
- ' ------------------------------------
- Dim writer As Stream = Request.GetRequestStream
- writer.Write(body, 0, body.Length)
- writer.Close()
-
- ' Réponse
- ' -----------------
- Dim response = Request.GetResponse()
- Dim reader As New IO.StreamReader(response.GetResponseStream())
- Dim ResponseBody = reader.ReadToEnd
- reader.Close()
- Response.Close()
-
- Console.WriteLine(ResponseBody) ' Le réponse du serveur
-
- Catch ex As Exception
- Console.WriteLine(ex.ToString)
- End Try
- End Sub
-
- Public Class MultipartFormDataBodyCreator
-
- ' Le Boundary délimitant chaque élément (Field) dans le corps du message
- ' On y met une valeure constante, mais normalement il faudrait s'assurer que cette
- ' chaine de caractère n'existe pas ailleur dans les données à envoyer (si c'est le cas, l'envois va planter)
- Public Property Boundary() As String
- Get
- Return _Boundary
- End Get
- Set(ByVal value As String)
- _Boundary = value
- End Set
- End Property
- Private _Boundary As String = "-----------------768299458641184676334"
-
- ' Les éléments à inclure dans le corps du message, tous défini par une paire Nom/Valeur
- ' Une propriété IsFileField as Bollean définie si c'est le champs du fichier à envoyer
- ' (dans quel cas Value contiendra le chemin du fichier à envoyer)
- Public Property Fields() As FieldList
- Get
- Return _Fields
- End Get
- Set(ByVal value As FieldList)
- _Fields = value
- End Set
- End Property
- Private _Fields As New FieldList
-
- ' Création d'un tableau de bytes contenant toutes les données à envoyer durant le request
- Public Function Create() As Byte()
-
- Dim encoding As New System.Text.ASCIIEncoding() ' Pour convertir le text en bytes
- Dim body(-1) As Byte ' Le contenu final
-
- ' Parcourir tous les champs pour créer le body
- For Each Field As Field In _Fields
- Dim sb As New System.Text.StringBuilder ' Pour créer les champs texte
- Dim FieldAsBytes() As Byte ' Le champs à ajouter au body en bytes
-
- ' Ajouter ce qui est commun aux champs FILE et les autres, c'est à dire une
- ' ligne avec le bundary puis la disposition et le nom du champ sur une autre ligne
- sb.AppendLine("--" & Boundary) ' Le bundary précédé de "--"
- sb.Append("Content-Disposition: form-data; name=""" & Field.Name & """") ' La disposition et le nom du champs (sans finir la ligne!)
-
- ' La suite dépend si c'est un fichier ou non
- If Field.IsFileField Then ' C'est un fichier
-
- ' Ajouter le nom du fichier sur la meme ligne que le nom du champ
- ' Sur les lignes suivantes, le type de contenu et 1 ligne vide (puis viendra le fichier)
- sb.AppendLine("; filename=""" & System.IO.Path.GetFileName(Field.Value) & """")
- sb.AppendLine("Content-Type: image/jpeg" & vbCrLf)
- FieldAsBytes = encoding.GetBytes(sb.ToString) ' Le 1er bout du champ codé en bytes
-
- ' On ajoute le contenu du fichier fichier au tableau FieldAsBytes
- Dim FileBytes() As Byte = IO.File.ReadAllBytes(Field.Value) ' Le contenu du fichier
- Array.Resize(FieldAsBytes, FieldAsBytes.Length + FileBytes.Length) ' Agrandir FieldAsBytes
- Array.Copy(FileBytes, 0, FieldAsBytes, sb.Length, FileBytes.Length) ' Ajouter le contenu du fichier
-
- ' Ajouter un vbcrlf pour finir le champ
- Array.Resize(FieldAsBytes, FieldAsBytes.Length + 2)
- Array.Copy(encoding.GetBytes(vbCrLf), 0, FieldAsBytes, FieldAsBytes.Length - 2, 2)
- Else
-
- ' C'est un élément du formulaire autre qu'un fichier
- sb.AppendLine("") ' Finir la ligne précédente
- sb.AppendLine(vbCrLf & Field.Value) ' Une ligne vide et la valeur
- FieldAsBytes = encoding.GetBytes(sb.ToString) ' Créer un tableau de Byte
-
- End If
-
- ' Ajouter la nouvelle entrée dans le body
- Dim CurrentBodySize As Integer = body.Length
- Array.Resize(body, body.Length + FieldAsBytes.Length) ' Agrandir le body
- Array.Copy(FieldAsBytes, 0, body, body.Length - FieldAsBytes.Length, FieldAsBytes.Length) ' Copier FieldAsBytes à la fin du body
- Next
- ' A la fin on ajoute le bundary et "--"
- Dim EndLine() As Byte = encoding.GetBytes("--" & Boundary & "--" & vbCrLf)
- Array.Resize(body, body.Length + EndLine.Length)
- Array.Copy(EndLine, 0, body, body.Length - EndLine.Length, EndLine.Length)
-
- Return body
- End Function
-
- End Class
- Public Class FieldList
- Inherits Generic.List(Of Field)
-
- ' Cette class éritée d'une collection générique List(Of Field) est créé juste pour ajouter les 2 procédures suivantes:
- Public Overloads Sub Add(ByVal Name As String, ByVal Value As String, Optional ByVal IsFileField As Boolean = False)
- MyBase.Add(New Field(Name, Value, IsFileField))
- End Sub
- Public Overloads Sub Insert(ByVal Index As Integer, ByVal Name As String, ByVal Value As String, Optional ByVal IsFileField As Boolean = False)
- MyBase.Insert(Index, New Field(Name, Value, IsFileField))
- End Sub
- End Class
- Public Class Field
- ' Représente un champs de formulaire
- Public IsFileField As Boolean = False
- Public Name As String = ""
- Public Value As String = ""
- Public Sub New()
-
- End Sub
- Public Sub New(ByVal Name As String, ByVal Value As String, Optional ByVal IsFileField As Boolean = False)
- Me.Name = Name
- Me.Value = Value
- Me.IsFileField = IsFileField
- End Sub
- End Class
' La requête se fait avec un HttpWebRequest, et on créé les données par la class que j'ai créé qui s'appel MultipartFormDataBodyCreator.
'
' Propriété et procédures publiques de MultipartFormDataBodyCreator:
'
' Public Sub Add(Name as String, _ ' Le nom du champs à ajouter
' Value as String, _ ' La valeur
' Optional IsFileField as Bollean = False") ' Si il s'agit d'un fichier, mettre IsFileField = true
' ' et Value = le chemin du fichier
'
' Public Property Boundary as String ' Le Boundary à utiliser (sert à délimiter les champs dans la requête)
'
' Public Function Create() as Byte() ' Retourne les données à envoyer sous forme d'un tableau de byte
'
'
' Pour envoyer la requête présenté juste au dessus, il faut donc faire comme ceci:
Private Sub SendFile()
Try
Dim bodyCreator As New MultipartFormDataBodyCreater ' La classe que j'ai créé et que je présente plus loins
Dim File as String = "C:\Image.jpg" ' Le fichier à envoyer
' Création des données à envoyer
' --------------------
bodyCreator.Fields.Add("uploadtype", "on")
bodyCreator.Fields.Add("fileupload", File, True) ' C'est le fichier
bodyCreator.Fields.Add("MAX_FILE_SIZE", "3145728")
bodyCreator.Fields.Add("refer", "http://site.com/")
bodyCreator.Fields.Add("brand", "")
bodyCreator.Fields.Add("optsize", "320x320")
Dim body() as Byte = bodyCreator.Create ' Les données à envoyer sous forme de tableau de Byte
Dim boundary as String = bodyCreater.Boundary ' Le boudary délimitant les champs. Utile pour créer la requête
' Création de la requête
' ------------------------
Dim Request as HttpWebRequest = HttpWebRequest.Create("http://site.com")
Request.Method = "POST"
Request.UserAgent = "Mozilla/5.0 (Windows; U; Windows NT 5.1; fr; rv:1.8.0.7) Gecko/20060909 Firefox/1.5.0.7"
Request.KeepAlive = True
Request.ServicePoint.Expect100Continue = False ' Important si le serveur ne gère pas les Expect
Request.Referer = "http://site.com/"
Request.ContentType = "multipart/form-data; boundary=" & boundary ' Ajouter le boundary après le Content-Type
Request.ContentLength = body.Length
' Ecriture des données
' ------------------------------------
Dim writer As Stream = Request.GetRequestStream
writer.Write(body, 0, body.Length)
writer.Close()
' Réponse
' -----------------
Dim response = Request.GetResponse()
Dim reader As New IO.StreamReader(response.GetResponseStream())
Dim ResponseBody = reader.ReadToEnd
reader.Close()
Response.Close()
Console.WriteLine(ResponseBody) ' Le réponse du serveur
Catch ex As Exception
Console.WriteLine(ex.ToString)
End Try
End Sub
Public Class MultipartFormDataBodyCreator
' Le Boundary délimitant chaque élément (Field) dans le corps du message
' On y met une valeure constante, mais normalement il faudrait s'assurer que cette
' chaine de caractère n'existe pas ailleur dans les données à envoyer (si c'est le cas, l'envois va planter)
Public Property Boundary() As String
Get
Return _Boundary
End Get
Set(ByVal value As String)
_Boundary = value
End Set
End Property
Private _Boundary As String = "-----------------768299458641184676334"
' Les éléments à inclure dans le corps du message, tous défini par une paire Nom/Valeur
' Une propriété IsFileField as Bollean définie si c'est le champs du fichier à envoyer
' (dans quel cas Value contiendra le chemin du fichier à envoyer)
Public Property Fields() As FieldList
Get
Return _Fields
End Get
Set(ByVal value As FieldList)
_Fields = value
End Set
End Property
Private _Fields As New FieldList
' Création d'un tableau de bytes contenant toutes les données à envoyer durant le request
Public Function Create() As Byte()
Dim encoding As New System.Text.ASCIIEncoding() ' Pour convertir le text en bytes
Dim body(-1) As Byte ' Le contenu final
' Parcourir tous les champs pour créer le body
For Each Field As Field In _Fields
Dim sb As New System.Text.StringBuilder ' Pour créer les champs texte
Dim FieldAsBytes() As Byte ' Le champs à ajouter au body en bytes
' Ajouter ce qui est commun aux champs FILE et les autres, c'est à dire une
' ligne avec le bundary puis la disposition et le nom du champ sur une autre ligne
sb.AppendLine("--" & Boundary) ' Le bundary précédé de "--"
sb.Append("Content-Disposition: form-data; name=""" & Field.Name & """") ' La disposition et le nom du champs (sans finir la ligne!)
' La suite dépend si c'est un fichier ou non
If Field.IsFileField Then ' C'est un fichier
' Ajouter le nom du fichier sur la meme ligne que le nom du champ
' Sur les lignes suivantes, le type de contenu et 1 ligne vide (puis viendra le fichier)
sb.AppendLine("; filename=""" & System.IO.Path.GetFileName(Field.Value) & """")
sb.AppendLine("Content-Type: image/jpeg" & vbCrLf)
FieldAsBytes = encoding.GetBytes(sb.ToString) ' Le 1er bout du champ codé en bytes
' On ajoute le contenu du fichier fichier au tableau FieldAsBytes
Dim FileBytes() As Byte = IO.File.ReadAllBytes(Field.Value) ' Le contenu du fichier
Array.Resize(FieldAsBytes, FieldAsBytes.Length + FileBytes.Length) ' Agrandir FieldAsBytes
Array.Copy(FileBytes, 0, FieldAsBytes, sb.Length, FileBytes.Length) ' Ajouter le contenu du fichier
' Ajouter un vbcrlf pour finir le champ
Array.Resize(FieldAsBytes, FieldAsBytes.Length + 2)
Array.Copy(encoding.GetBytes(vbCrLf), 0, FieldAsBytes, FieldAsBytes.Length - 2, 2)
Else
' C'est un élément du formulaire autre qu'un fichier
sb.AppendLine("") ' Finir la ligne précédente
sb.AppendLine(vbCrLf & Field.Value) ' Une ligne vide et la valeur
FieldAsBytes = encoding.GetBytes(sb.ToString) ' Créer un tableau de Byte
End If
' Ajouter la nouvelle entrée dans le body
Dim CurrentBodySize As Integer = body.Length
Array.Resize(body, body.Length + FieldAsBytes.Length) ' Agrandir le body
Array.Copy(FieldAsBytes, 0, body, body.Length - FieldAsBytes.Length, FieldAsBytes.Length) ' Copier FieldAsBytes à la fin du body
Next
' A la fin on ajoute le bundary et "--"
Dim EndLine() As Byte = encoding.GetBytes("--" & Boundary & "--" & vbCrLf)
Array.Resize(body, body.Length + EndLine.Length)
Array.Copy(EndLine, 0, body, body.Length - EndLine.Length, EndLine.Length)
Return body
End Function
End Class
Public Class FieldList
Inherits Generic.List(Of Field)
' Cette class éritée d'une collection générique List(Of Field) est créé juste pour ajouter les 2 procédures suivantes:
Public Overloads Sub Add(ByVal Name As String, ByVal Value As String, Optional ByVal IsFileField As Boolean = False)
MyBase.Add(New Field(Name, Value, IsFileField))
End Sub
Public Overloads Sub Insert(ByVal Index As Integer, ByVal Name As String, ByVal Value As String, Optional ByVal IsFileField As Boolean = False)
MyBase.Insert(Index, New Field(Name, Value, IsFileField))
End Sub
End Class
Public Class Field
' Représente un champs de formulaire
Public IsFileField As Boolean = False
Public Name As String = ""
Public Value As String = ""
Public Sub New()
End Sub
Public Sub New(ByVal Name As String, ByVal Value As String, Optional ByVal IsFileField As Boolean = False)
Me.Name = Name
Me.Value = Value
Me.IsFileField = IsFileField
End Sub
End Class
Conclusion
Cette classe à été créé selon mes besoins, et elle n'est pas capable de créer n'importe quelle requête. Notament le contenu du fichier (Content-Type:) est dans mon code fixé à "image/jpeg", donc je ne sais pas ce que ca donne si on envois autre chose. En tout cas, dans mon cas ca fonctionne. Si vous avez d'autre type de champs à envoyer qui ne sont pas géré dans cette classe, ca vous permet tout de même d'avoir une base qui fonctionne. Attention, le boundary est fixé à "-----------------768299458641184676334" dans mon code. Normalement il faut s'assurer que la chaine de caratères formant le boundary ne soit présente nul part ailleur dans les données à envoyer, car si c'est le cas, la requête échouera. Le contenu du fichier est entièrement lu pour créer les données à envoyer, puis envoyé en 1 seul bloc. Cette méthode ne convient donc pas pour l'envois de gros fichiers, dans quel cas il faudrait découper le fichier en petites partie et les envoyer les une après les autres. Voilà, c'est tout. J'espère que ce code sera utile à certains, car j'ai vu bcp de gens chercher à faire ca, mais très souvent sans avoir eu la réponse :-) PS. Je ne donne pas de zip, car mon projet est nettement plus gros et j'utilise cette classe en multi-threading (dans un BackGroundWorker). J'ai juste retapé la procédure SendFile pour vous faire part de l'usage.
Sources du même auteur
Sources de la même categorie
Sources en rapport avec celle ci
Commentaires et avis
Discussions en rapport avec ce code source dans le forum
enregistrer et ouvrir plusieur richtextbox dans un formulaire [ par gillesrun ]
bonjour,je debute sur bv 2005 et je crée un petit programme de comptabilité de point destiné à. mon sportpour cela, j'utilise plusieur combobox pour m
Affichage Notepas dans un formulaire [ par tri_yann ]
Bonjour,A la suite d'un traitement, Access crée en local un fichier log. Je voudrai pouvoir rendre le contenu de ce fichier facilement consultable dan
IPM.Post [ par dthuler ]
Bonjour !Voilà mon problème; j'ai créé un formulaire Outlook 2003 personnalisé de classe IPM.Note et dois le poster à l'aide d'un bouton dans un dossi
Checkbox, formulaire et requete [ par benzin69 ]
Bonjour à tous! je vais essayer de faire clair. J'ai une sorte de petit programme access à réaliser et une partie de celui-ci porte sur un système de
ouverture formulaire a l'ouverture du fichier EXCEL [ par pnt ]
Bonjour à tous et toutes,Comment peut écrire en code VBA :Lors de l'ouverture d'un fichier "Excel1", s'affiche un formulaire "Saisie_Données" qui récu
EVENEMENT [ par cretthie ]
Bonjour tout le monde !voila, j'ai dans mon application 1 formulaire qui possede 3 user controle. mes usercontrol sont initialise lors de l'evenement
Gestion d'exception dans un Sub New()... [ par spilett ]
Je développe en VB.NET sur SharpDevelop.Dans une appli je souhaite ouvrir et lire un fichier dans le constructeur Sub New() d'un Formulaire (c'est-à-d
access formulaire sur requetes actions [ par pilaf ]
bonjour a tous,je recherche a saisir dans un formulaire contenant un sous-formulaire basé sur une requete action.le probleme est que cette deuxieme re
Requete sous access [ par aaliyan ]
Voilà, je voudrais executer une requete sous access, mais cela ne fonctionne pas sur le update, voila le code : Fichier = "'" & Fichier & "'"
Formulaire Outlook [ par PonPon_SB ]
Bonjour à tous ! Dans le cadre de mon boulot je dois développer un formulaire Outlook pour faire des saisies de données. Le formulaire de ba
|
Téléchargements
Logiciels à télécharger sur le même thème :
|