1
 45

Écrire des données sur un serveur

   

Cliquez dans l'image ou appuyez sur le bouton pour charger un fichier à partir de votre espace disque.

Sélectionnez un fichier JPG, PNG, GIF ou SVG. NOTE : La taille maximum du fichier est configurée à 1 Mo et la largeur maximum de l'affichage à 320 px.

Ouvrez un dossier contenant des images avec l'explorateur de votre système de fichiers. Faites glisser et déposer un fichier JPG, PNG, GIF ou SVG sur l'image.

Éditez la largeur ou la hauteur de l'image.

Essayez les boutons Défaire et Refaire.

Rechargez la page. Les modifications sont enregistrées.

Pour le code de l'éditeur, consultez les pages du manuel ImageSizeInspector et Editor et l'article Architecture d'un éditeur.

Cliquez sur le bouton pour télécharger l'image avec ses dimensions sur le serveur.

Le fichier est transféré par blocs de 100 ko. La progression est affichée par un pourcentage. Quand le transfert est terminé, une coche est affichée à côté du bouton. Une erreur est signalée par un point d'exclamation en rouge.

Code

L'image est copiée dans l'espace disque du serveur en réponse à un POST par une fonction qui en extrait le type MIME et la taille du fichier, la largeur et la hauteur de l'image, le bloc de données qui est transféré, sa taille et sa position dans le fichier. Un bloc de données est encodé en BASE64.

Dans iZend, le fichier uploadimage.php s'installe directement dans le dossier actions. Pour activer l'URL /uploadimage dans un site web, une entrée est ajoutée dans le fichier aliases.inc à la liste des URLs communes à toutes les langues.

$aliases = array(
    array(
        'captcha' => 'captcha',
...
        'uploadimage' => 'uploadimage',
    ),
    'en'    => array(
...
    ),
);

Adaptez l'installation de cette fonction à votre environnement de développement.

  1. define('TMP_DIR', ROOT_DIR . DIRECTORY_SEPARATOR . 'tmp');
  2.  
  3. define('IMAGE_MAX_SIZE', 1000000);

Définit TMP_DIR - le dossier dans lequel le fichier de l'image est sauvegardé - au nom du sous-dossier tmp dans le dossier racine du site et IMAGE_MAX_SIZE - la taille maximum d'un image - à 1 Mo.

  1. function uploadimage($lang, $arglist=false) {

Définit la fonction uploadimage qui est appelée par l'URL /uploadimage du site. Les arguments $lang - la langue du contenu, qui sera toujours false - et $arglist - les paramètres passés dans l'URL - ne sont pas utilisés.

  1.     $maxfilesize=IMAGE_MAX_SIZE;
  2.  
  3.     $filetypes=array('image/jpeg', 'image/png', 'image/gif', 'image/svg+xml');

Initialise $maxfilesize à IMAGE_MAX_SIZE et $filetypes à la liste de types MIME supportés.

  1.     $type=$data=false;
  2.     $size=$offset=0;
  3.  
  4.     $width=$height=0;

Initialise les variables des arguments du POST.

  1.     if (isset($_POST['file_size'])) {
  2.         $size=$_POST['file_size'];
  3.     }
  4.     if (isset($_POST['file_type'])) {
  5.         $type=$_POST['file_type'];
  6.     }
  7.     if (isset($_POST['file_offset'])) {
  8.         $offset=$_POST['file_offset'];
  9.     }
  10.     if (isset($_POST['file_data'])) {
  11.         $data=base64_decode($_POST['file_data'], true);
  12.     }
  13.     if (isset($_POST['image_width'])) {
  14.         $width=$_POST['image_width'];
  15.     }
  16.     if (isset($_POST['image_height'])) {
  17.         $height=$_POST['image_height'];
  18.     }

Extrait les arguments du POST. Le bloc de données encodé en BASE64 est décodé.

  1.     if (($width = filter_var($width, FILTER_VALIDATE_INT, array('options' => array('min_range' => 0)))) === false)
  2.         goto badrequest;
  3.  
  4.     if (($height = filter_var($height, FILTER_VALIDATE_INT, array('options' => array('min_range' => 0)))) === false)
  5.         goto badrequest;
  6.  
  7.     if (($offset = filter_var($offset, FILTER_VALIDATE_INT, array('options' => array('min_range' => 0)))) === false)
  8.         goto badrequest;
  9.  
  10.     if (($size = filter_var($size, FILTER_VALIDATE_INT, array('options' => array('min_range' => 0, 'max_range' => $maxfilesize))))  === false)
  11.         goto badrequest;
  12.  
  13.     if (!$type or !in_array($type, $filetypes)) {
  14.         goto badrequest;
  15.     }

Vérifie les arguments du POST.

  1.     if (!$data) {
  2.         goto badrequest;
  3.     }
  4.  
  5.     $datasize=strlen($data);
  6.  
  7.     if ($offset + $datasize > $size) {
  8.         goto badrequest;
  9.     }

Vérifie la taille des données envoyées dans le POST.

  1.     goto trashfile;

Sur ce serveur, l'écriture du fichier est simulée. Pour vérifier que l'image a bien été copiée en l'affichant, commentez cette instruction et adaptez le reste du code pour enregistrer le fichier dans un dossier accessible en écriture par le serveur.

  1.     $name='image';
  2.  
  3.     switch ($type) {
  4.         case 'image/jpeg':
  5.             $fname = $name . '.jpg';
  6.             break;
  7.         case 'image/png':
  8.             $fname = $name . '.png';
  9.             break;
  10.         case 'image/gif':
  11.             $fname = $name . '.gif';
  12.             break;
  13.         case 'image/svg+xml':
  14.             $fname = $name . '.svg';
  15.             break;
  16.         default:
  17.             goto badrequest;
  18.     }
  19.  
  20.     $file = TMP_DIR . DIRECTORY_SEPARATOR . $fname;

Définit le nom du fichier de l'image. L'extension du nom du fichier dépend du type MIME de l'image.

  1.     $fout = @fopen($file, $offset == 0 ? 'wb' : 'cb');
  2.  
  3.     if ($fout === false) {
  4.         goto internalerror;
  5.     }

Ouvre le fichier de l'image en mode binaire. Si le POST contient le premier bloc de données, le fichier est créé.

  1.     $r = fseek($fout, $offset);
  2.  
  3.     if ($r == -1) {
  4.         goto internalerror;
  5.     }

Déplace le pointeur du fichier à la position du bloc de données.

  1.     $r = fwrite($fout, $data);
  2.  
  3.     if ($r === false) {
  4.         goto internalerror;
  5.     }

Écrit le bloc de données.

  1.     if ($offset + $datasize < $size) {
  2.         return false;
  3.     }

Retourne un simple entête 200 Ok si plus de blocs de données sont attendus.

  1.     $file = TMP_DIR . DIRECTORY_SEPARATOR . $name . '.json';
  2.  
  3.     $data = array('width' => $width, 'height' => $height, 'name' => $fname);
  4.  
  5.     $r = @file_put_contents($file, json_encode($data));
  6.  
  7.     if ($r === false) {
  8.         goto internalerror;
  9.     }
  10.  
  11.     return false;

Si toutes les données de l'image ont été sauvegardées, crée un fichier image.json indiquant la largeur et la hauteur de l'image et le nom du fichier. Retourne un simple entête 200 Ok.

  1. trashfile:
  2.     return false;

Retourne un simple entête 200 Ok sans enregistrer les données.

  1. badrequest:
  2.     header('HTTP/1.1 400 Bad Request');
  3.     return false;

Retourne un simple entête 400 Bad Request si un des éléments du POST est invalide.

  1. internalerror:
  2.     header('HTTP/1.1 500 Internal Error');
  3.     return false;
  4. }

Retourne un simple entête 500 Internal Error si l'image n'a pas pu être correctement sauvegardée.

  1. <?php $debug=false; ?>

Mettre $debug à true permet d'accéder dans la console du navigateur à tous les composants de l'interface. Si $debug vaut false, tout le code en JavaScript est protégé par une fonction de fermeture.

  1. <?php $filetypes=array('image/jpeg', 'image/png', 'image/gif', 'image/svg+xml'); ?>
  2. <?php $maxsize=1000000; ?>
  3. <?php $imgname='image'; ?>
  4. <?php $imgsrc='/files/images/loadimage.png'; ?>
  5. <?php $imgwidth=320; ?>
  6. <?php $imgheight=180; ?>
  7. <?php $uploadurl='/uploadimage'; ?>
  8. <?php $chunksize=100000; ?>

Définit les types MIME supportés par l'éditeur, la taille maximum d'une image, le nom de l'item de stockage dans le navigateur, l'URL de l'image initiale, la largeur et la hauteur de l'affichage de l'image, l'URL de la fonction de sauvegarde du serveur, la taille des blocs de données envoyés au serveur.

  1. <?php $id=uniqid('id'); ?>

Définit l'identifiant de la <div> qui encadre l'affichage de l'éditeur.

  1. #<?php echo $id; ?> .ojs img {
  2.     max-width: <?php echo $imgwidth; ?>px;
  3. }
  4. #<?php echo $id; ?> #uploadstatus {
  5.     font-size: smaller;
  6. }

Configure la largeur de l'affichage de l'image. Montre la progression d'un transfert dans une fonte plus petite.

  1. <div id="<?php echo $id; ?>" class="noprint">
  2. <div class="ojs">
  3. <div>
  4. <div class="ojs_undo">
  5. <button type="submit" class="ojs_button narrow control_undo" disabled><i class="fas fa-undo"></i></button>
  6. <button type="submit" class="ojs_button narrow control_redo" disabled><i class="fas fa-redo"></i></button>
  7. </div>
  8. <span class="ojs_dimension">
  9. <input class="ojs_width" type="number" min="0"/>&nbsp;<i class="fas fa-arrows-alt-h small"></i>
  10. <input class="ojs_height" type="number" min="0"/>&nbsp;<i class="fas fa-arrows-alt-v small"></i>
  11. </span>
  12. </div>
  13. <div><img src="<?php echo $imgsrc; ?>" alt="" width="<?php echo $imgwidth; ?>" height="<?php echo $imgheight; ?>"/></div>
  14. <div>
  15. <span><button id="loadimage" type="submit" class="ojs_button narrow"><i class="fas fa-file-image"></i></button></span>
  16. <span><button id="uploadimage" type="submit" class="ojs_button narrow"><i class="fas fa-file-export"></i></button></span>
  17. <span id="uploadstatus"></span>
  18. </div>
  19. </div>
  20. </div>

Crée les widgets pour les instances de UndoPanel et de ImageSizeInspector, le conteneur de l'image et les boutons pour charger un fichier local et le télécharger sur le serveur.

  1. <?php head('javascript', '/objectivejs/Objective.js'); ?>
  2. <?php head('javascript', '/objectivejs/Responder.js'); ?>
  3. <?php head('javascript', '/objectivejs/View.js'); ?>
  4. <?php head('javascript', '/objectivejs/Model.js'); ?>
  5. <?php head('javascript', '/objectivejs/Validator.js'); ?>
  6. <?php head('javascript', '/objectivejs/Editor.js'); ?>
  7. <?php head('javascript', '/objectivejs/Inspector.js'); ?>
  8. <?php head('javascript', '/objectivejs/NumberInspector.js'); ?>
  9. <?php head('javascript', '/objectivejs/DimensionInspector.js'); ?>
  10. <?php head('javascript', '/objectivejs/ImageInspector.js') ?>
  11. <?php head('javascript', '/objectivejs/SequenceInspector.js') ?>
  12. <?php head('javascript', '/objectivejs/ImageSizeInspector.js') ?>
  13. <?php head('javascript', '/objectivejs/ImageModel.js'); ?>
  14. <?php head('javascript', '/objectivejs/Undo.js'); ?>
  15. <?php head('javascript', '/objectivejs/Panel.js'); ?>
  16. <?php head('javascript', '/objectivejs/UndoPanel.js'); ?>
  17. <?php head('javascript', '/objectivejs/ModelStorageDelegate.js'); ?>

Inclut le code de toutes les classes nécessaires. RAPPEL : La fonction head de la librairie iZend ajoute une balise telle que <script src="/objectivejs/Objective.js"></script> à la section <head> du document HTML. Adaptez le code à votre environnement de développement.

  1. function ImageLoader(model, inspectors, panel, upload, status) {
  2.     Editor.call(this, model, null, inspectors, panel);
  3.  
  4.     if (upload)
  5.         upload.onclick = () => this.upload();
  6.  
  7.     this._upload = upload;
  8.     this._status = status;
  9. }
  10.  
  11. ImageLoader.prototype = Object.create(Editor.prototype);
  12.  
  13. Object.defineProperty(ImageLoader.prototype, 'constructor', { value: ImageLoader, enumerable: false, writable: true });
  14.  
  15. ImageLoader.uploadURL = '/uploadimage';
  16. ImageLoader.chunkSize = 100000;
  17.  
  18. ImageLoader.prototype.upload = function() {
  19.     const {image} = this._model.get();
  20.  
  21.     if (image.image === null)
  22.         return;
  23.  
  24.     const [url] = image.image;
  25.     const [width, height] = image.size;
  26.  
  27.     const imgtype = /^data:(image\/[-.+0-9a-zA-Z]+);base64,/.exec(url)[1];
  28.     const imgdata = atob(url.substring(url.indexOf(',')+1));
  29.     const imgsize = imgdata.length;
  30.  
  31.     const uploadurl = ImageLoader.uploadURL;
  32.     const chunksize = ImageLoader.chunkSize;
  33.  
  34.     let offset = 0;
  35.     let progress = 0;
  36.  
  37.     const uploadslice = () => {
  38.         if (this._status && imgsize > chunksize)
  39.             this._status.innerText = `${progress}%`;
  40.  
  41.         const data = imgdata.substring(offset, offset + chunksize);
  42.  
  43.         $.post(uploadurl, {file_size: imgsize, file_type: imgtype, file_offset: offset, file_data: btoa(data), image_width: width, image_height: height })
  44.             .done(() => {
  45.                 offset += data.length;
  46.                 progress = Math.floor((offset / imgsize) * 100);
  47.  
  48.                 if (progress < 100)
  49.                     uploadslice();
  50.                 else {
  51.                     if (this._status)
  52.                         this._status.innerHTML = '<i class="fas fa-check-circle"></i>';
  53.                     if (this._upload)
  54.                         this._upload.disabled = false;
  55.                 }
  56.             })
  57.             .fail(() => {
  58.                 if (this._status)
  59.                     this._status.innerHTML = '<i class="fas fa-exclamation-circle inerror"></i>';
  60.                 if (this._upload)
  61.                     this._upload.disabled = false;
  62.             });
  63.     };
  64.  
  65.     if (this._status)
  66.         this._status.innerText = '';
  67.  
  68.     if (this._upload && imgsize > chunksize)
  69.         this._upload.disabled = true;
  70.  
  71.     uploadslice();
  72. }

Une instance de ImageLoader est un Editor qui peut télécharger une image avec une taille sur un serveur. upload récupère les données du modèle, extrait le type de l'image et les données de l'image en BASE64, en calcule la taille, transmet au serveur toutes ces informations et les données de l'image par blocs séquentiels de 100 ko, affiche le statut de l'opération.

  1. <?php if (!$debug): ?>
  2. (function() {
  3. <?php endif; ?>

Isole tout le code en JavaScript dans une fonction de fermeture si $debug vaut false.

  1.     const model = new ImageModel('<?php echo $imgname; ?>');
  2.  
  3.     const container = document.querySelector('#<?php echo $id; ?>');
  4.  
  5.     const panel = new UndoPanel();
  6.  
  7.     panel.setManagedWidget(container.querySelector('.ojs_undo')).resetWidget();
  8.  
  9.     const filetypes = [<?php echo implode(',', array_map(function($s) { return "'$s'"; }, $filetypes)); ?>];
  10.  
  11.     const imageInspector = new ImageInspector(null, { filetypes: filetypes, maxsize: <?php echo $maxsize; ?> });
  12.  
  13.     imageInspector.setManagedWidget(container.querySelector('img'));
  14.  
  15.     const sizeInspector = new DimensionInspector(0, 0, {minWidth: 0, minHeight: 0});
  16.  
  17.     sizeInspector.setManagedWidget(container.querySelector('.ojs_dimension')).resetWidget();
  18.  
  19.     const imageSizeInspector = new ImageSizeInspector(imageInspector, sizeInspector);
  20.  
  21.     const loadimage = container.querySelector('#loadimage');
  22.  
  23.     loadimage.onclick = () => imageInspector.loadImage();
  24.  
  25.     const inspectors = {
  26.         image:      imageSizeInspector
  27.     };
  28.  
  29.     const uploadimage = container.querySelector('#uploadimage');
  30.     const uploadstatus = container.querySelector('#uploadstatus');
  31.  
  32.     const editor = new ImageLoader(model, inspectors, panel, uploadimage, uploadstatus);
  33.  
  34.     model.setDelegate(new ModelStorageDelegate());
  35.  
  36.     model.readIn();
  37.  
  38.     model.enableSync();

Crée le modèle de données à éditer. Récupère la <div> qui encadre le HTML du programme. Crée le panneau avec les boutons pour défaire et refaire une modification, crée l'inspecteur de l'image et de sa taille, crée le bouton indépendant qui permet de charger une image, crée l'éditeur. Associe le modèle à un délégué en charge de la sauvegarde de ses données dans l'espace de stockage du navigateur. Configure le modèle avec les données déjà enregistrées. Active la sauvegarde des données du modèle dès modification.

  1. <?php if (!$debug): ?>
  2. })();
  3. <?php endif; ?>

Ferme la fonction qui isole le code en JavaScript si $debug vaut false.

VOIR AUSSI

ImageModel, ModelStorageDelegate, ImageSizeInspector, Editor, Wall, Architecture d'un éditeur, Configurer des graphiques par Plotly

Commentaires

Votre commentaire :
[p] [b] [i] [u] [s] [quote] [pre] [br] [code] [url] [email] strip aide 2000

Entrez un maximum de 2000 caractères.
Améliorez la présentation de votre texte avec les balises de formatage suivantes :
[p]paragraphe[/p], [b]gras[/b], [i]italique[/i], [u]souligné[/u], [s]barré[/s], [quote]citation[/quote], [pre]tel quel[/pre], [br]à la ligne,
[url]http://www.izend.org[/url], [url=http://www.izend.org]site[/url], [email]izend@izend.org[/email], [email=izend@izend.org]izend[/email],
[code]commande[/code], [code=langage]code source en c, java, php, html, javascript, xml, css, sql, bash, dos, make, etc.[/code].