IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

VBA et développement Web

Image non disponible


précédentsommairesuivant

VI. Requêtes HTTP (MSXML2 ou WinHTTP)

MSXML2 et WinHTTP possèdent des objets similaires.

MSXML2.XMLHTTP et WinHttp.WinHttpRequest fonctionnent de manière équivalente pour leur méthode open et send par exemple.
L'objet de la librairie XML possède en plus une propriété responseXML qui est bien sûr absente de l'objet de la librairie WinHTTP.

Par contre la gestion du proxy est différente :
- MSXML2 utilise la configuration par défaut du proxy : dans le registre : HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Internet Settings ;
- WinHTTP n'utilise par défaut aucun proxy.

De plus, WinHTTP.WinHttpRequest possède des événements facilement exploitables.

Pour simplifier l'article, nous écrirons le code qui suit pour la librairie WinHTTP.
Les méthodes utilisées seront les mêmes avec la librairie MSXML2, sauf fonctionnalité non existante.

Le proxy est à paramétrer selon les indications du chapitre suivant pour tous les exemples utilisant WinHTTP.

VI-A. Ouvrir une connexion

Pour MSXML2 c'est facile, nous l'avons vu dans le chapitre précédent.
Cette librairie utilise le paramétrage du proxy par défaut de Windows.

 
Sélectionnez
Dim oXMLHTTP As MSXML2.XMLHTTP
Set oXMLHTTP = New MSXML2.XMLHTTP
oXMLHTTP.Open "POST", "http://monurl", False


Pour WinHTTP c'est presque aussi facile.

 
Sélectionnez
Dim oWinHTTP As WinHttp.WinHttpRequest
Set oWinHTTP = New WinHttp.WinHttpRequest
oWinHTTP.Open "POST", "http://monurl", False


Si ce code fonctionne à la maison, il ne fonctionnera pas sur un réseau d'entreprise avec un proxy.
Il faut préciser le proxy et éventuellement l'utilisateur et le mot de passe.

 
Sélectionnez
Dim oWinHTTP As WinHttp.WinHttpRequest
Set oWinHTTP = New WinHttp.WinHttpRequest
oWinHTTP.setProxy 2, "http=monproxy:8080;https=monproxyssl:443;ftp=monproxyftp:8080"
oWinHTTP.Open "POST", "http://monurl", False
oWinHTTP.SetCredentials "monutilisateur", "monmotdepasse", 1


Deux lignes ont fait leur apparition :
- setProxy définit l'adresse du proxy : il est possible de définir des proxy différents en fonction du protocole (http, ftp...).
Le 2 est la valeur de la constante HTTPREQUEST_PROXYSETTING_PROXY ;
- SetCredentials définit l'utilisateur et le mot de passe : il est possible que ces informations soient optionnelles en fonction de la configuration de votre réseau.
Le 1 est la valeur de la constante HTTPREQUEST_SETCREDENTIALS_FOR_PROXY.

VI-B. Télécharger un fichier

Pour télécharger un fichier, nous allons tout simplement ouvrir une connexion sur ce fichier et récupérer le contenu.
Nous allons télécharger le logo de cet article.

 
Sélectionnez
Function DownloadHTTP()
Dim oWinHTTP As WinHttp.WinHttpRequest
Set oWinHTTP = New WinHttp.WinHttpRequest
oWinHTTP.Open "POST", "http://arkham46.developpez.com/articles/office/officeweb/images/logoofficeweb.jpg", False
oWinHTTP.send
End Function


Placez un point d'arrêt à la fin de la fonction et regardez dans la fenêtre Variables locales.
Le contenu du fichier a été placé dans les propriétés response* de l'objet oXMLHTTP.
Ce contenu est incompréhensible car c'est le contenu brut du fichier image.

Nous allons utiliser la propriété responseBody qui est un tableau de bytes.

Image non disponible

Cette propriété peut être écrite dans un fichier grâce aux fonctions de fichiers de VBA.

 
Sélectionnez
Function DownloadHTTP()
Dim oWinHTTP As WinHttp.WinHttpRequest
Dim fic As Integer
Dim buffer() As Byte
Set oWinHTTP = New WinHttp.WinHttpRequest
oWinHTTP.Open "POST", "http://arkham46.developpez.com/articles/office/officeweb/images/logoofficeweb.jpg", False
oWinHTTP.send
If oWinHTTP.Status = 200 Then
   fic = FreeFile
   Open "c:\monimage.jpg" For Binary As #fic
   buffer = oWinHTTP.responseBody
   Put #fic, , buffer
   Erase buffer
   Close #fic
End If
End Function

On utilise une variable buffer pour transférer temporairement le contenu du fichier car le put écrit le responseBody différemment selon les cas.
Il peut ajouter un descripteur qui rend le fichier corrompu. Utiliser un buffer corrige ce problème.

Autre méthode d'écriture de fichier : Comment faire pour écrire des données dans un fichier à l'aide de WriteFile API.


Voici donc un moyen bien pratique pour télécharger un fichier.
Notez qu'il n'y a malheureusement pas d'état d'avancement du téléchargement avec la librairie MSXML2.
Le chapitre suivant montre comment voir l'état d'avancement d'un téléchargement avec la librairie WinHttp.

Il est cependant possible d'utiliser la technique vue au chapitre V-D-2 pour télécharger un fichier de manière asynchrone avec MSXML2.

VI-C. Télécharger un fichier avec affichage de la progression


Ce chapitre est incompatible avec la librairie MSXML2.

La librairie WinHTTP est requise.

En effet l'objet WinHTTP.WinHttpRequest expose différents événements :
- OnError : une erreur est survenue... ;
- OnResponseStart : la requête commence à alimenter la réponse ;
- OnResponseDataAvailable : des données ont alimenté la réponse (tout ou une partie) ;
- OnResponseFinished : la requête a terminé d'alimenter la réponse.

Pour bénéficier de ces événements, il faut écrire le code dans un module de classe (ou un module d'un formulaire).

Nous allons créer un formulaire de téléchargement :
Créez un nouveau formulaire contenant :
- un bouton btnDownload : ce bouton lancera le téléchargement ;
- une zone de texte txtProgress : cette zone de texte affichera l'avancement.

En remplacement de la zone de texte vous pouvez simuler une barre de progression avec une étiquette colorée dont vous ferez varier la taille.


Un objet WinHttpRequest doit être déclaré en en-tête de module :

 
Sélectionnez
Private WithEvents oWinHTTP As WinHTTP.WinHttpRequest


Ensuite sur clic sur le bouton, on lance le téléchargement asynchrone.

 
Sélectionnez
Private Sub btnDownload_Click()
Set oWinHTTP = New WinHTTP.WinHttpRequest
oWinHTTP.Open "POST", "http://arkham46.developpez.com/articles/office/officeweb/fichiers/officeweb.zip", True
oWinHTTP.send
End Sub


Ajoutez éventuellement le paramétrage de votre proxy.

J'ai mis ici une adresse d'un fichier en dur, on pourrait la mettre dans une variable ou la lire dans une table Access ou une feuille Excel, ou la récupérer d'une variable selon le besoin.

L'écriture du fichier se fait lorsque la requête est terminée.
Dans les listes déroulantes en haut du module, choisissez oWinHTTP et OnResponseFinished.

Le code d'écriture du fichier dans cet événement est le même que celui vu dans les chapitres précédents :

 
Sélectionnez
Private Sub oWinHTTP_OnResponseFinished()
Dim fic As Integer
Dim buffer() As Byte
If oWinHTTP.Status = 200 Then
   fic = FreeFile
   Open "C:\telechargement.zip" For Binary As #fic
   buffer = oWinHTTP.responseBody
   Put #fic, , buffer
   Erase buffer
   Close #fic
End If
End Sub

Autre méthode d'écriture de fichier : Comment faire pour écrire des données dans un fichier à l'aide de WriteFile API.


Le nom du fichier téléchargé peut être différent de celui du serveur.

Le fichier est bien téléchargé, ajoutons maintenant l'affichage de la progression.

Dans les listes déroulantes en haut du module, choisissez oWinHTTP et OnResponseDataAvailable :

 
Sélectionnez
Private Sub oWinHTTP_OnResponseDataAvailable(Data() As Byte)
            
End Sub


Cet événement nous transmet un tableau contenant la partie de la réponse qui vient d'être chargée.
Nous pouvons cumuler la taille de ce tableau pour obtenir la progression du téléchargement.

Pour faire les calculs, nous avons besoin de déclarer deux variables en en-tête de module.

 
Sélectionnez
Private TotalSize As Long
Private CurrentSize As Long


- TotalSize est la taille totale à télécharger ;
- CurrentSize est la taille déjà téléchargée.

Dans les listes déroulantes en haut du module, choisissez oWinHTTP et OnResponseStart.
Dans cet événement qui marque le début du téléchargement :
- nous initialisons la taille déjà téléchargée : CurrentSize = 0 ;
- nous lisons la taille totale à télécharger : cette lecture se fait dans l'en-tête de la requête.

 
Sélectionnez
Private Sub oWinHTTP_OnResponseStart(ByVal Status As Long, ByVal ContentType As String)
CurrentSize = 0
TotalSize = oWinHTTP.getResponseHeader("Content-Length")
End Sub


On peut maintenant revenir à l'événement OnResponseDataAvailable :

 
Sélectionnez
Private Sub oWinHTTP_OnResponseDataAvailable(Data() As Byte)
CurrentSize = CurrentSize + UBound(Data) - LBound(Data) + 1
Me.txtProgress.Value = Format(CurrentSize / TotalSize, "0.00%")
End Sub


On ajoute à la taille téléchargée la taille du tableau de l'événement.
Et on calcule et affiche le pourcentage d'avancement.

Vous pouvez maintenant lancer le téléchargement et voir la progression en pourcentage.

Quelques améliorations sont à prévoir :
- gérer les erreurs dans l'événement onError ;
- gérer les cas éventuels où la taille totale ne serait pas disponible ;
- l'écriture pourrait se faire au fur et à mesure de la réception des données pour pouvoir gérer de très gros fichiers.

Mieux encore, on pourrait créer un module de classe réutilisable pour gérer facilement nos téléchargements.

L'utilisation de cette librairie s'avère en tout cas bien plus aisée que les API WinInet sur lesquelles elle s'appuie.

VI-D. Utilisation d'un langage serveur (PHP)

Pour ce chapitre, nous allons écrire un peu de PHP.
PHP est un langage qui s'exécute sur le serveur, et qui va nous être très utile pour dialoguer entre internet et VBA.

Si vous n'avez aucune connaissance en PHP, vous pouvez consulter les cours PHP.

En résumé :
- un fichier contenant du code php doit être déposé sur un serveur sachant exécuter du php (ce qui est logique...) ;
- vous pouvez tester vos pages php sur votre pc en installant une solution tout en main telle que Wamp ou EasyPhp ;
- il est possible d'écrire du code html et php sur la même page : le php s'exécute sur le serveur, puis le html sur le client ;
- on a pour habitude de mettre l'extension .php à un fichier contenant du php mais ce n'est pas obligatoire ;
- le code php s'intègre entre les balises <?php et ?> ;
- pour écrire à partir de php dans une page html, utilisez echo ;
- une variable php est préfixée par un $ ($mavariable = "toto") ;
- chaque ligne de code doit se terminer par un point-virgule ;
- utilisez // pour une ligne de commentaires ;
- internet regorge de documentations et d'exemples.

Mon choix s'est porté sur PHP car il est largement utilisé (on trouve donc de l'aide facilement) et assez accessible.
On pourrait également dialoguer avec un autre langage, tel que ASP. Ne connaissant pas le sujet, je n'ai développé que PHP dans cet article.

VI-D-1. Passage de paramètres dans une chaîne


Vous avez sans doute déjà vu des adresses de cette forme :

http://monURL/index.php?data1=valeur1&data2=valeur2


Cela signifie que nous passons deux valeurs à la page :
- data1=valeur1
- data2=valeur2

Écrivons un fichier php1.php contenant ce code :

 
Sélectionnez
<?php
// Lecture des variables
$data1 = $_POST['data1'];
$data2 = $_POST['data2'];
// Écriture des variables
echo "$data1\n";
echo "$data2\n";
?>


Comme c'est notre premier code php, nous allons le décortiquer.

D'abord le code php est inséré entre les balises <?php et ?>.
Ensuite on récupère les données avec l'instruction $_POST.
On écrit enfin chaque donnée avec l'instruction echo.
Petite subtilité de php : on peut inclure une variable dans une chaîne de caractères.
Le \n est un retour à la ligne.

Une écriture différente du echo pourrait utiliser l'opérateur de concaténation qui est le point en php :

 
Sélectionnez
echo $data1."\n";


Placez ce fichier sur un serveur php.
Pour déposer un fichier sur un serveur, vous pouvez utiliser par exemple FileZilla
J'ai déposé ce fichier ici : https://arkham46.developpez.com/articles/office/officeweb/fichiersphp/php1.php.

Appelons ce fichier avec une requête HTTP :

 
Sélectionnez
Function SendPhp1()
Dim oWinHTTP As WinHTTP.WinHttpRequest
Set oWinHTTP = New WinHTTP.WinHttpRequest
oWinHTTP.Open "POST", "http://arkham46.developpez.com/articles/office/officeweb/fichiersphp/php1.php", False
oWinHTTP.setRequestHeader "Content-Type", "application/x-www-form-urlencoded"
oWinHTTP.send "data1=valeur1&data2=valeur2"
Debug.Print oWinHTTP.responseText
End Function


Si besoin, paramétrez votre proxy comme vu au paragraphe VI-A.

On ouvre toujours une connexion avec open, et on envoie la requête avec send.
Entre les deux, une instruction setRequestHeader a fait son apparition.
On indique avec "application/x-www-form-urlencoded" que l'on va passer des paramètres.
Ces paramètres sont indiqués dans la commande send.
Chacun des paramètres est séparé par un &.

On affiche enfin la réponse du serveur contenue dans responseText.
Cette réponse est ce que nous écrivons en php avec echo.

Exécutez la fonction SendPhp1, le résultat s'affiche dans la fenêtre exécution.

valeur1
valeur2


Peu utile en l'état, le code php peut facilement être modifié pour par exemple écrire un fichier contenant les données, ou les inscrire dans une base de données MySQL par exemple.

Pour finir, on peut essayer d'envoyer des données issues de variables.
On va également insérer un saut de ligne dans une des variables pour tester si ça passe.

 
Sélectionnez
Function SendPhp1()
Dim oWinHTTP As WinHTTP.WinHttpRequest
Dim sData1 As String
Dim sData2 As String
Set oWinHTTP = New WinHTTP.WinHttpRequest
oWinHTTP.Open "POST", "http://arkham46.developpez.com/articles/office/officeweb/fichiersphp/php1.php", False
oWinHTTP.setRequestHeader "Content-Type", "application/x-www-form-urlencoded"
sData1 = "Test" & vbCrLf & "sur 2 lignes"
sData2 = "1 ligne"
oWinHTTP.send "data1=" & sData1 & "&data2=" & sData2
Debug.Print oWinHTTP.responseText
End Function


On a ajouté deux variables sData1 et sData2 qui contiennent du texte.
La variable sData1 contient un saut de ligne (vbcrlf).
On doit alors construire une chaîne de caractères pour insérer nos variables et envoyer le tout avec send.
Exécutez la fonction SendPhp1, le résultat s'affiche dans la fenêtre exécution.

Test
sur 2 lignes
1 ligne


Pas de souci donc pour insérer un saut de ligne.

Essayons à présent de mettre des caractères accentués, dans sData1 par exemple :

 
Sélectionnez
sData1 = "Test de caractères accentués"


Le résultat n'est pas correct :

Test de caract�res accentués
1 ligne


Il faut dire qu'on n'a rien précisé quant au contenu de la reponse.
Dans le fichier php il faut ajouter un en-tête, juste après le <?php :

 
Sélectionnez
<?php
header('Content-Type: text/plain; charset=UTF-8');
[...]


On indique ainsi qu'on retourne un fichier texte brut (text/plain) avec un encodage UTF-8 qui gère correctement les accents.

Les valeurs possibles de la valeur de Content-Type sont listées ici : MIME Media Types.


Autre souci possible : mettre des guillemets dans une variable :

 
Sélectionnez
sData1 = "Test de ""guillemets"""


Le résultat peut ne pas être correct non plus :

Test de \"guillemets\"
1 ligne


En fonction de l'installation de php, celui-ci peut éventuellement ajouter des caractères d'échappement (\).
Pour les retirer, utilisez en php la fonction stripslashes sur les variables reçues.

 
Sélectionnez
<?php
header('Content-Type: text/plain; charset=UTF-8');
// Lecture des variables
$data1 = $_POST['data1'];
$data2 = $_POST['data2'];
if(get_magic_quotes_gpc()) // Si les magic quotes sont activés
{
    $data1 = stripslashes($data1);
    $data2 = stripslashes($data2);
 }
// Écriture des variables
echo "$data1\n";
echo "$data2\n";
?>


Reste un problème si vous souhaitez utiliser un caractère & dans une variable.
Comme c'est un caractère réservé pour la séparation des données, on ne peut pas l'utiliser dans une des valeurs.

VI-D-2. Passage de paramètres multiples complexes


Plutôt que de passer tous les paramètres sur une même ligne, on peut passer des données structurées.
Il faut utiliser un type de contenu : "multipart/form-data".

Il est alors possible d'envoyer des données de type complexe.
Les valeurs possibles du type de donnée (Content-Type) sont listées ici : MIME Media Types.

On crée une nouvelle fonction SendPhp2 qui est assez proche de la précédente :

 
Sélectionnez
Function SendPhp2()
Dim oWinHTTP As WinHTTP.WinHttpRequest
Dim sData1 As String
Dim sData2 As String
Dim sFormData As String
Const csBoundary As String = "A@poXX125"
Set oWinHTTP = New WinHTTP.WinHttpRequest
oWinHTTP.Open "POST", "http://arkham46.developpez.com/articles/office/officeweb/fichiersphp/php1.php", False
oWinHTTP.setRequestHeader "Content-Type", "multipart/form-data; charset=""UTF-8"";boundary=" + csBoundary
sData1 = "Test de ""guillemets"""
sData2 = "caractères spéciaux <$£^??çço ©"
sFormData = ???
oWinHTTP.send sFormData
Debug.Print oWinHTTP.responseText
End Function


Comme toujours on ouvre d'abord une connexion avec open.
On cible pour cet exemple le même fichier php que pour le chapitre précédent.

Le Content-Type de la méthode setRequestHeader a changé, c'est maintenant : multipart/form-data.
On a ajouté également (séparé par un point-virgule) le type de caractères utilisé : UTF-8.
Si on ne précise pas UTF-8, les caractères accentués ne passeront pas.
Il faut ensuite définir une chaîne de séparation des données, appelée boundary.
Cette valeur est arbitraire : il faut juste s'assurer que cette chaîne de caractères n'apparaît pas dans les données.

Ensuite il faut envoyer nos données que nous allons écrire dans la variable texte sFormData.
Ces données doivent être écrites selon un format très précis : W3C Recommendation.

Pour les données que l'on souhaite envoyer, voici ce que doit contenir sFormData :

--A@poXX125
Content-Disposition: form-data; name="data1"
Content-Type: text/plain

Test de "guillemets"
--A@poXX125
Content-Disposition: form-data; name="data2"
Content-Type: text/plain

caractères spéciaux <$£^??çço ©
--A@poXX125--


Chaque valeur commence par la valeur de boundary définie en en-tête de la requête, précédée de deux tirets.
Pour chacune de ces valeurs, il faut définir la disposition (form-data) et le nom de la variable qui sera récupérée en php.
Puis vient le type de contenu, optionnel car text/plain par défaut.
On peut utiliser tout type MIME connu, pourvu qu'on sache le gérer à l'arrivée sur le serveur.

Attention, il faut ensuite un saut de ligne (vbCrLf) avant de mettre le contenu de la donnée.
La fin de la donnée est marquée par une nouvelle valeur boundary précédée par deux tirets.
Ce qui marque en fait le début de la donnée suivante.

Pour la dernière donnée, on marque la fin en ajoutant deux tirets (donc deux tirets avant la boundary et deux tirets après).

On peut facilement écrire ces données dans une variable :

 
Sélectionnez
sFormData = "--" & csBoundary & vbCrLf & _
       "Content-Disposition: form-data; name=""data1""" & vbCrLf & _
       "Content-Type: text/plain" & vbCrLf & _
       vbCrLf & _
       sData1 & vbCrLf & _
       "--" & csBoundary & vbCrLf & _
       "Content-Disposition: form-data; name=""data2""" & vbCrLf & _
       "Content-Type: text/plain" & vbCrLf & _
       vbCrLf & _
       sData2 & vbCrLf & _
       "--" & csBoundary & "--"


Le serveur nous renvoie nos valeurs (par les echo du php) :

Test de "guillemets"
caractères spéciaux <$£^??çço ©


À noter :
- pas de souci avec cette méthode si la valeur contient un caractère & par exemple ;
- il est toujours indispensable de retirer les slashes avec stripslashes comme vu au chapitre précédent.

Pour une utilisation un peu plus poussée de cette méthode, voir les deux chapitres suivants.

VI-D-3. Envoi (upload) d'un fichier

Permettre l'envoi d'un fichier au serveur n'est pas sans danger.
Une personne mal intentionnée pourrait envoyer sur votre serveur un fichier contenant du code malveillant.
Il convient donc de prendre certaines précautions.
Voici une page très intéressante sur ce sujet : Secure File Upload Check List With PHP.

Pour des raisons évidentes, je n'ai pas placé le fichier php d'upload sur mon serveur.
Pour tester le code qui suit, vous devrez placer le fichier sur votre serveur php et modifier son adresse dans le code VBA.

Envoyer un fichier sur le serveur avec php peut s'avérer très utile.
Pas besoin d'un accès FTP avec les codes d'accès, il suffit d'un accès à la page php, et un accès en écriture par php sur le répertoire voulu.
Il faudra tout de même faire quelques vérifications pour ne pas laisser l'utilisateur uploader n'importe quoi !

Pour plus d'informations sur l'upload de fichier en php, voici un article très intéressant dont nous allons nous inspirer : Upload de fichiers en PHP.

Cet article explique la création d'un formulaire HTML et le code PHP qui se charge de l'upload.
Notre travail ici sera principalement de remplacer le formulaire HTML par du code VBA.

Pour envoyer un fichier, il faut utiliser un type de contenu "multipart/form-data" comme vu au chapitre précédent :

 
Sélectionnez
Function SendFilePhp()
Dim oWinHTTP As WinHTTP.WinHttpRequest
Dim sFormData As String
Const csBoundary As String = "A@poXX125"
Set oWinHTTP = New WinHTTP.WinHttpRequest
oWinHTTP.Open "POST", "http://arkham46.developpez.com/articles/office/officeweb/fichiersphp/phpupload.php", False
oWinHTTP.setRequestHeader "Content-Type", "multipart/form-data; charset=""UTF-8"";boundary=" + csBoundary
sFormData = ???
oWinHTTP.send sFormData
Debug.Print oWinHTTP.responseText
End Function


Le code php est écrit dans un fichier phpupload.php.
Pour commencer, nous allons ajouter une seule ligne dans ce fichier.

 
Sélectionnez
<?php print_r($_FILES); ?>


Cette ligne écrit les propriétés des fichiers envoyés.
Cela ne suffit pas à gérer l'upload complet, nous écrirons plus tard la suite du code php.

Reste à remplir le plus gros morceau du code VBA : les données dans sFormData.
Voici la structure de ces données :

--A@poXX125
Content-Disposition: form-data; name="fichier1"; filename="monfichier.ext"
Content-Type: application/octet-stream

... contenu du fichier ...
--A@poXX125--


Ces données doivent être écrites selon un format très précis : W3C Recommendation.

On retrouve la valeur boundary qui délimite les données.

Le nom de notre variable est "fichier1".
On lui précise un nom de fichier avec l'attribut filename.
C'est cet attribut filename qui distingue un upload de fichier d'un simple envoi de données.

Le type de contenu est application/octet-stream, c'est-à-dire un contenu binaire.
Si on connaît le type de contenu envoyé, on peut le préciser ici : par exemple image/gif.

Ensuite vient le contenu du fichier, car le chemin donné dans filename n'est qu'une information et ne demande pas la lecture du contenu du fichier.
Et c'est ici que ça se complique.
On souhaite envoyer un contenu binaire, c'est-à-dire le contenu brut du fichier.
Passer par une variable texte (as String) risque de ne pas conserver ces données à l'état brut.
Nous allons donc utiliser un tableau d'octets :

 
Sélectionnez
Function SendFilePhp()
Dim oWinHTTP As WinHTTP.WinHttpRequest
Dim sFormData() As Byte
Dim sFormDataHeader As Variant
Dim sFormDataFile As Variant
Dim sFormDataFooter As Variant
Const csBoundary As String = "A@poXX125"
Const sFileName As String = "C:\monimage.jpg"
Set oWinHTTP = New WinHTTP.WinHttpRequest
oWinHTTP.Open "POST", "http://arkham46.developpez.com/articles/office/officeweb/fichiersphp/phpupload.php", False
oWinHTTP.setRequestHeader "Content-Type", "multipart/form-data; charset=""UTF-8"";boundary=" + csBoundary
sFormData = ???
oWinHTTP.send sFormData
Debug.Print oWinHTTP.responseText
End Function


Le contenu final que nous enverrons au serveur (sFormData) est un tableau (car on a mis des parenthèses).
Il est dynamique, c'est-à-dire qu'on n'a pas indiqué sa taille.
Et il contient des éléments de type Byte (Octet), c'est le plus petit élément disponible en VB.

On a également déclaré trois variables :
- sFormDataHeader : contiendra le contenu avant les données du fichier ;
- sFormDataFile : contiendra le contenu du fichier ;
- sFormDataFooter : contiendra le contenu après les données du fichier.

sFormDataHeader et sFormDataFooter seront des chaînes de caractères.
sFormDataFile sera un tableau d'octets.
Ces trois variables sont déclarées de type Variant, cela nous permettra de les concaténer.

Notez aussi l'ajout d'une constante sFileName qui contient le nom du fichier du PC à uploader.
Nous nous contentons ici d'une simple constante.
Il est toutefois possible d'ouvrir une boîte de dialogue de recherche de fichier :
- avec Les boîtes de dialogues intégrées Excel ;
- avec Les boîtes de dialogues intégrées Access (à partir de 2007) ;
- ou avec des API si la version utilisée n'a pas de boîtes de dialogue intégrées : API GetOpenFileName.

On commence par écrire l'en-tête des données :

 
Sélectionnez
sFormDataHeader = "--" & csBoundary & vbCrLf & _
       "Content-Disposition: form-data; name=""fichier1"";filename=""" & sFileName & """" & vbCrLf & _
       "Content-Type: application/octet-stream" & vbCrLf & _
       vbCrLf


Comme nous allons envoyer un tableau d'octets, il ne faut pas qu'il soit au format Unicode (format des chaînes de caractères VB).
On doit donc ajouter une conversion :

 
Sélectionnez
sFormDataHeader = StrConv(sFormDataHeader, vbFromUnicode)


Puis on écrit de la même manière la fin des données (ce qu'il y a après le contenu du fichier) :

 
Sélectionnez
sFormDataFooter = vbCrLf & _
       "--" & csBoundary & "--"
sFormDataFooter = StrConv(sFormDataFooter, vbFromUnicode)


Occupons-nous maintenant des données du fichier.
Nous chargeons le fichier dans un tableau d'octets avec les fonctions de fichier VBA :

 
Sélectionnez
' Retourne le contenu brut d'un fichier (tableau d'octets)
Function GetFileBinary(FileName) As Variant
   Dim buffer() As Byte
   Dim f As Integer
   f = FreeFile
   Open FileName For Binary As #f
   ReDim buffer(1 To LOF(f))
   Get #f, , buffer
   Close #f
   GetFileBinary = buffer
End Function


Cette fonction peut être placée dans le module du formulaire ou dans un module séparé.

On ouvre le fichier avec For Binary pour préciser qu'on va lire le contenu brut et non pas un fichier texte séquentiel.
Le buffer est un tableau d'octets que l'on redimensionne à la taille du fichier avant de le remplir.
On place enfin ce buffer dans le retour de la fonction.

On peut alors ajouter l'écriture des données du fichier dans la variable sFormDataFile :

 
Sélectionnez
sFormDataFile = GetFileBinary(sFileName)


Pas de conversion à faire ici, on laisse les données brutes.

Pour finir la construction des données, on concatène le tout :

 
Sélectionnez
sFormData = sFormDataHeader & sFormDataFile & sFormDataFooter


La concaténation est possible entre des tableaux d'octets et des chaînes de caractères si les variables sont déclarées en Variant.

Voici la fonction complète :

 
Sélectionnez
Function SendFilePhp()
Dim oWinHTTP As WinHTTP.WinHttpRequest
Dim sFormData() As Byte
Dim sFormDataHeader As Variant
Dim sFormDataFile As Variant
Dim sFormDataFooter As Variant
Const csBoundary As String = "A@poXX125"
Const sFileName As String = "C:\monimage.jpg"
Set oWinHTTP = New WinHTTP.WinHttpRequest
oWinHTTP.Open "POST", "http://arkham46.developpez.com/articles/office/officeweb/fichiersphp/phpupload.php", False
oWinHTTP.setRequestHeader "Content-Type", "multipart/form-data; boundary=" + csBoundary
            
sFormDataHeader = "--" & csBoundary & vbCrLf & _
       "Content-Disposition: form-data; name=""fichier1"";filename=""" & sFileName & """" & vbCrLf & _
       "Content-Type: application/octet-stream" & vbCrLf & _
       vbCrLf
sFormDataHeader = StrConv(sFormDataHeader, vbFromUnicode)
sFormDataFooter = vbCrLf & _
       "--" & csBoundary & "--"
sFormDataFooter = StrConv(sFormDataFooter, vbFromUnicode)
sFormDataFile = GetFileBinary(sFileName)
            
sFormData = sFormDataHeader & sFormDataFile & sFormDataFooter
oWinHTTP.send sFormData
Debug.Print oWinHTTP.responseText
End Function
            
' Retourne le contenu brut d'un fichier (tableau d'octets)
Function GetFileBinary(FileName) As Variant
   Dim buffer() As Byte
   Dim f As Integer
   f = FreeFile
   Open FileName For Binary As #f
   ReDim buffer(1 To LOF(f))
   Get #f, , buffer
   Close #f
   GetFileBinary = buffer
End Function


Lorsqu'on l'exécute on récupère ceci dans la fenêtre Exécution :

Array
(
[fichier1] => Array
(
[name] => monimage.jpg
[type] => application/octet-stream
[tmp_name] => /tmp/phplXJMz2
[error] => 0
[size] => 7127
)

)


C'est le contenu de la variable $_FILES renvoyé par le php.

On voit que c'est un tableau (cf. le premier Array) qui contient un fichier ([fichier1]).
Les propriétés de ce fichier sont elles aussi dans un tableau dans lequel on trouve :
- name : le nom du fichier : notez que le chemin n'est pas inclus ;
- type : le type du fichier : c'est ce qu'on a mis dans content-type ;
- tmp_name : le nom temporaire du fichier téléchargé : nous allons nous y intéresser de près ;
- error : un éventuel code d'erreur ;
- size : la taille du fichier.

Le nom temporaire du fichier est très important.
En fait le fichier est uploadé dans un répertoire particulier.
Ce fichier est temporaire, il faut le déplacer ensuite à son emplacement final (sinon il sera détruit).

Pour déplacer un tel fichier, il faut utiliser la fonction php move_uploaded_file :

 
Sélectionnez
<?php print_r($_FILES); ?>
<?php 
if (move_uploaded_file($_FILES['fichier1']['tmp_name'], $_FILES['fichier1']['name']))
    { echo "Le fichier ". $_FILES['fichier1']['name']. " a été uploadé"; }
else
    { echo "Erreur lors de l'upload"; } 
            
?>


Ici on déplace le fichier 'fichier1' à partir de son emplacement temporaire.
On le déplace dans le même répertoire que le script php.

L'exécution de la fonction SendFilePhp retourne ceci :

Array
(
[fichier1] => Array
(
[name] => monimage.jpg
[type] => application/octet-stream
[tmp_name] => /tmp/phpoXPnTg
[error] => 0
[size] => 7127
)

)
Le fichier monimage.jpg a été uploadé


Le fichier a donc été chargé sur le serveur.

N'oubliez pas de mettre en place des vérifications de sécurité sur le fichier uploadé pour ne pas permettre à chacun d'envoyer n'importe quoi sur votre serveur.
Rapprochez-vous si nécessaire d'un expert php qui saura mieux que moi vous aider sur ce sujet.

VI-D-4. Ouverture vers d'autres possibilités

Nous savons maintenant envoyer et recevoir des données à l'aide de php.
Pour exploiter ces données, on peut envisager de les stocker dans une base de données MySQL par exemple.
Vous trouverez de la documentation sur le sujet dans les cours mysql pour php.

PHP est également capable d'écrire des fichiers sur le serveur pour stocker les données.
Voici quelques fonctions basiques de gestion de fichiers pour lesquelles vous pouvez facilement trouver de la documentation : fopen, fputs, fclose.

Retrouvez tous les cours php


précédentsommairesuivant

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2011 Thierry GASPERMENT. Aucune reproduction, même partielle, ne peut être faite de ce site ni de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts. Droits de diffusion permanents accordés à Developpez LLC.