Stockage de fichiers dans MySql par PHP
Transcription
Stockage de fichiers dans MySql par PHP
Stockage de fichiers dans des tables MYSQL avec PHP Rédacteur: Alain Messin CNRS UMS 2202 Admin06 30/06/2006 Le but de ce document est de donner les principes de manipulation de fichiers dans une table mysql, à partir de php. On va partir d'un fichier existant, avec comme exemple un fichier gif. On suppose le fichier déposé à un endroit accessible; ici il sera dans le dossier du script php. On suppose une base de donnée mysql créée et accessible par un nom d'utilisateur et un mot de passe. Dans ce document on verra comment stocker ces fichiers dans une table mysql, comment l'afficher dans une page web et comment en regénérer un fichier. Tous les exemples donnés sont fonctionnels, mais sont limités aux instructions permettant le fonctionnement de l'exemple, en excluant toute considération « environnementale ». Pour autant, toutes les étapes seront commentées de façon à fournir un maximum d'aide. Selon les versions php et mysql utilisées, il peut-être nécessaire de revoir les scripts. Stockage du fichier dans une table mysql: Nous utiliserons des types de données spéciaux, qui sont les BLOBS (binary large objects). Création de la table: Nous allons créer la table « fichiers » ou seront stockés les fichiers; il faut exécuter la commande SQL de création de la table suivante: CREATE TABLE `fichiers` ( `Id` int(11) NOT NULL auto_increment, `Titre` varchar(50) default NULL, `Donnees` longblob, `Type` varchar(50) default NULL, PRIMARY KEY (`Id`), UNIQUE KEY `id` (`Id`) ) ; Cette commande peut-être exécutée en mode ligne ou via l'interface phpMyadmin de notre serveur mysql, ou encore incluse dans un script php; on ne détaillera pas ce point. La table ainsi créée comporte un identifiant Id, un Titre pour le fichier stocké (qui peut également être le nom du fichier), les données du fichier, ainsi que le type de données. Le type blob des données indique que ces données sont stockées sans modification (liés à une table de caractères, suppression des blancs ou autre), contrairement à ce qui peut se passer pour des types du genre varchar ou text; longblob indique aussi la taille maximum des données qui peuvent être stockées (tinyblob <256 octets, blob<65536, longblob<4294967296!). Attention, les données sont stockées non compressées, et demande donc l'espace disque adéquat. De plus, il faut tenir compte des capacités d'échanges entre le client et le serveur. Il est donc conseillé de tester les tailles limites Stockage de fichiers dans des tables MYSQL avec PHP 1/5 acceptables dans votre configuration et suivant vos besoins, de limiter vos ambitions ou d'adapter les paramètres client/serveur (max_allowed_packet, memory_limit...) et/ou votre configuration matérielle. Enfin, tenez compte des temps de transmission vers vos utilisateurs! Pour de petites images, le type blob peut-être suffisant. Stockage du fichier dans la table: Il faut tout d'abord se connecter à la base mysql ( « ma_base »); nous devrons définir quelque part les données de connexion, c'est à dire l'adresse du serveur MySql (« serveur.mondomaine »), le nom d'utilisateur (« mon_nom »)et le mot de passe associé (« mon_mot_de_passe »). Nous définirons également le nom de la table contenant les fichiers (« fichiers ») et le nom du fichier à stocker (« fichier »): Connexion à la base: define define define define define define ("MACHINE", "serveur.mondomaine") ; ("UTILISATEUR", "mon_nom") ; ("BASE", "ma_base") ; ("MOT_DE_PASSE", "mon_mot_de_passe") ; ("TABLE", "fichiers") ; ("FICHIER", "image.gif") ; Les définitions précédente devront être mises de préférence dans un include non accessible du serveur web, pour des raisons de sécurité. Ici, nous inclurons ces définitions dans le script unique pour des raisons de clarté. $c = @mysql_connect(MACHINE, UTILISATEUR, MOT_DE_PASSE) ; if ( $c == 0 ) { die ("Serveur inaccessible\n") ; } Le @ évite l'apparition de messages d'erreurs en cas de non connexion. A noter que l'identifiant de connexion $c peux être omis dans les appels aux fonctions mysql si une seule connexion est active. if ( mysql_select_db(BASE) == false ) die ("Base de donnée inaccessible\n") ; A ce point nous sommes connectés à la base et nous pouvons manipuler les données. Préparation des données: Nous devons lire le fichier dans une variable: $titre = 'Fichier test'; $type = 'image/gif'; if (!$r=@fopen(FICHIER, "r")) die("Erreur d'accès au fichier ".FICHIER."\n") ; $size=filesize(FICHIER); $donnees = addslashes(fread($r, $size)); Après avoir défini le titre et le type du fichier (nous reviendrons en annexe sur la détermination du type de fichier), on ouvre le fichier par l'instruction fopen qui retourne une ressource de gestion de fichier; on détermine ensuite la taille du fichier. Pour le fopen, on utilise aussi le @ pour éviter les messages d'erreurs éventuels, erreurs que l'on gère en testant le retour (erreur si retour faux) et en terminant le script avec un message d'erreur adapté si besoin. La lecture des données suit avec la fonction fread (de préférence utiliser file_get_contents qui permet de meilleures performances) qui Stockage de fichiers dans des tables MYSQL avec PHP 2/5 lit le fichier dans la variable $donnees. Ici on n'a pas inhibé un possible message d'erreur, car le fichier est accessible. Par contre la fonction addslashes est indispensable pour ajouter aux données les « \ »qui permettent de constituer une commande SQL d'insertion dans la table non perturbée par les éventuels apostrophes ou autres caractères sensibles contenus dans le fichier. Insertion dans la table: Il ne reste plus qu'à former et exécuter le requête SQL d'insertion dans la table: $req= "INSERT INTO ".TABLE." ( Id, Titre, Donnees, Type )". " VALUES ('$id','$titre','$donnees','$type')"; mysql_query($req) or die("Pas moyen d'ajouter le fichier à la table !!!"); A ce stade, nous avons réussi à stocker un fichier dans une table de la base MySql. On peut penser à récupérer l'identifiant pour une utilisation ultérieure, puisque la fonction d'autoincrément de mysql génère automatiquement un nouvel Id à chaque insertion. $id= mysql_insert_id(); print "Numéro d'identifiant dans la table: $id<br>\n"; Récupération d'un fichier stocké dans une table mysql: Nous voulons maintenant récupérer le fichier stocké dans la table, nous pouvons penser à deux utilisations: – affichage par le navigateur – création d'un fichier sur le serveur web affichage par le navigateur: Nous appellerons le script d'affichage en ajoutant ?id=1 au nom du script, comme si ce script avait été appelé par l'exécution d'un formulaire avec la méthode « get », pour lire le fichier numéro 1. Nous commencerons notre script avec les mêmes instructions de connexion à la base que pour le stockage du fichier; ces instructions ne seront donc pas redites ici. $id=intval($_GET['id']); // un minimum de securite ! $req= "SELECT Titre, Donnees, Type ". "FROM ".TABLE." where Id='$id'"; $r = @mysql_query($req ) or die ("Erreur: pas d'accès au fichier"); if(mysql_num_rows($r) == 1) { $type = @mysql_result($r, 0, "Type"); $donnees = @mysql_result($r, 0, "Donnees"); header("Content-type: $type"); echo $donnees; } else { echo "Erreur, pas trouvée le fichier"; }; On commence donc par lire l'identifiant demandé, avec une conversion en nombre entier simplement pour éviter qu'un plaisantin tente de forger une requête SQL non prévue en mettant ?id=chaine_de_caracteres_pas_sympa. Stockage de fichiers dans des tables MYSQL avec PHP 3/5 On exécute ensuite la requête qui sélectionne le fichier ayant pour identifiant 1 dans la table (on suppose que l'on connaît les identifiant,on peut aussi sélectionner sur d'autres critères évidemment. Un message d'erreur et l'arrêt du script sont prévus en cas de requête non aboutie. Si tout se passe bien une ressource $r est renvoyée. Il ne reste qu'à tester que la requête n'a renvoyée qu'une seule image par la fonction mysql_num_rows et à récupérer les valeurs du type et des données du fichier à afficher dans les variables $type et $donnees. L'affichage lui-même consiste en l'envoi du header indiquant au navigateur le type du fichier envoyer, ce qui permet au dit navigateur de savoir comment l'afficher, puis d'envoyer les données par un simple echo. Attention: lors de l'envoi d'un header, il ne faut pas qu'un caractère ait été envoyé sur la page. Un casse-tête fréquent est lorsqu'un espace en tête du script php ou dans un include a été interprété comme du html, et donc envoyé tel que vers le navigateur, d'où l'apparition du message : « Warning: Cannot add header information - headers already sent by ... ». création d'un fichier: Il faut bien sur vérifier que le compte sous lequel tourne votre serveur web possède les droits d'écriture dans la zone voulue. Pour créer le fichier, il suffit de reprendre le même script que pour l'affichage, mais en le modifiant légèrement: -tout d'abord le type est inutile, puisque l'on va simplement créer un fichier, sans se préoccuper de savoir comment le gérer. -ensuite on va créer le fichier tout simplement, en utilisant par exemple le champ Titre dans la table pour lui donner son nom. $zone_accessible='/usr/ma_zone_accessible/'; $id=intval($_GET['id']); // un minimum de securite ! $req= "SELECT Titre, Donnees ". "FROM ".TABLE." where Id='$id'"; $r = @mysql_query($req ) or die ("Erreur: pas d'accès au fichier"); if(mysql_num_rows($r) == 1) { $titre = @mysql_result($r, 0, "Titre"); $donnees = @mysql_result($r, 0, "Donnees"); echo $zone; if (!$r=@fopen($zone_accessible.$titre, "w")) die("Erreur: pas pu créer $titre<br>\n"); fwrite($r,$donnees); fclose($r); echo "Fichier '$titre' créé dans $zone_accessible<br>\n"; } else { echo "Erreur, pas trouvé le fichier"; }; On définit donc $zone_accessible comme étant le chemin de création du fichier. La lecture de la table s'effectue comme auparavant, sans le type. On crée ensuite le fichier en concaténant le nom de la zone et le titre récupéré dans la base (attention au manque de / ou double //!!), ceci avec l'option w de fopen, avec message d'erreur si la création échoue. fopen retourne une ressource $r si tout est bon. Un fwrite écrit les données dans le fichier (en php5, on pourra utiliser file_put_contents ). En cas d'erreur du fwrite (disque plein par exemple!), le script s'arrête avec un message. fclose ferme le fichier. Stockage de fichiers dans des tables MYSQL avec PHP 4/5 Annexe: Détermination des types de fichiers et gestion des content-type Lorsqu'un fichier doit être traité, le logiciel de traitement doit savoir comment traiter les données qu'il contient. Certains logiciels ne peuvent traiter qu'un format de données, et ne se posent donc pas de question. D'autres au contraire, comme les navigateurs, les afficheurs d'images ou les logiciels de traitement de texte, doivent pouvoir traiter différemment les données suivant leur format: - pages html, images jpeg, video, microsoft word, wordperfect, pdf etc... Pour cela, différents moyens sont utilisés, certains pouvant être combinés: - association par l'extension: c'est ce qui se passe quand windows vous demande avec quoi ouvrir tel document; vous choisissez un logiciel adapté et windows associe l'extension du fichier au logiciel en question. Tous les fichiers seront donc traités par ce logiciel, qui appliquera un traitement standard ou qui précisera son traitement par la méthode ci-dessous: - reconnaissance d'une zone d'en tête: en général, les fichiers commencent par une zone d'en tête spécifique à leur format; en ouvrant avec un éditeur quelconque un fichier gif, on remarquera le GIF89 en début de fichier; %PDF-1.4 sera l'en tête d'un fichier pdf etc... Grâce à cette entête le logiciel saura comment traiter les données suivant leur format. -indication du mime type, notamment par l'indication content-type=... Des valeurs comme: text/html, image/png, image/gif, video/mpeg, text/css , application/x-excel sont connues, et sont associées aussi à des extensions « standards ». Pour revenir à notre sujet, nous avons indiqué dans notre script de mise en table du fichier: $type='image/gif' Pour cela, nous avons simplement exprimé que nous savions que le fichier traité était un fichier gif, son type mime étant alors image/gif. Nous avons alors indiqué au navigateur dans l'entête de la réponse HTTP à l'interrogation du script: header("Content-type: $type"); indiquant ainsi au navigateur quoi faire des données qui allaient suivre, car le navigateur possède une table de correspondance entre les types de données et l'action à déclencher. Pour autant, si l'on sait comment indiquer quoi faire des données, comment déterminer le type de données lorsqu'un fichier est inconnu, ou que l'on veut pouvoir traiter différents types de fichiers? Il ne semble pas y avoir de réponse vraiment universelle, mais plutôt des moyens d'y arriver dans presque tous les cas: - examen de l'extension (s'il y en a une, et si elle correspond bien au type de fichier). - décodage de l'entête (si on se limite à quelques cas connus!). - détermination du type de données par les fonctions du module php fileinfo, si installé, qui utilise un fichier magic.mime pour tenter de déterminer le type du fichier. - utilisation du content-type fourni en cas d' upload depuis un navigateur. Le type file d'un élément input de formulaire dont enctype="multipart/form-data" comme ci-dessous: <FORM enctype="multipart/form-data" ACTION="script.php" METHOD="POST"> <input type="file" name="toto" size="35"> permet, lors de l'envoi, de récupérer le type de fichier, bien sur fourni par le navigateur. $type = $_FILES['toto']['type']; Stockage de fichiers dans des tables MYSQL avec PHP 5/5