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

Astuces Dev Oracle

[Actualité] Créer et Modifier un fichier zip en PL/SQL

Noter ce billet
par
McM
, 28/07/2015 à 19h34 (7379 Affichages)
En cherchant à générer un zip en plsql sans passer par du java ou par l'OS, je suis tombé sur le bon site de Anton Scheffer
Website: http://technology.amis.nl/blog qui fournit un code permettant de créer un fichier zip contenant plusieurs fichiers dans un BLOB : Parsing a Microsoft Word docx, and unzip zipfiles, with PL/SQL

C'était une excellente base de départ, mais on ne pouvait que créer un fichier zip, puis le lire, mais pas moyen de maintenir.

1/ Analyse
Comprendre comment est généré un fichier zip, très simple en fait, chaque fichier (compressé) est concaténé au précédent, et tout à la fin, on met les infos globales du zip et de tous les fichiers.
Donc pour rajouter un fichier, il suffit de supprimer les infos globales, concaténer son fichier compressé et recréer les infos globales.
J'ai donc créé la procédure INIT_ZIP qui modifie le Blob Zippé en supprimant les infos globales [appelé Central Directory], après avoir mémorisé le commentaire.

2/ Débugages

a) Après chargement de mes zip créés sous linux (par un zip -j), j'avais un plantage lorsque je rajoutais des fichiers
Il s'avère que dans le zip créé sous Linux, l'extrafield est différent entre l'entête et le directory (8 bytes de plus)
Correction de FINISH_ZIP

b) J'ai eu un plantage en prod car un fichier faisait 0 octet.
Debug Fonction FILE2BLOB si la source est à 0 octet

3/ Cosmétique
Ajout d'un paramètre Compression (add1file),
Ajout des informations Date Maj, size_comp et size_uncomp dans File_List

Voici le script finalisé : as_zip.sql

4/ Etape finale : L'écran Forms afin de lister les fichiers zip, télécharger les fichiers compressés, ou les zip
Le seul problème que j'ai eu a été sur la gestion de la récupération de la liste du zip avec un filtre par nom directement depuis le package As_zip qui me plantait Forms.

J'ai donc créé une fonction de type pipelined et de faire un curseur pour renseigner le bloc liste
Code sql : Sélectionner tout - Visualiser dans une fenêtre à part
SELECT filename, datemaj, size_comp, size_uncomp FROM TABLE (F_LISTING_ZIP(:archives.nomdoc, :q1.nomfic))

Code sql : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
CREATE OR REPLACE TYPE TYP_REC_VARCHAR AS OBJECT (filename VARCHAR2(2000), datemaj DATE, size_comp NUMBER, size_uncomp NUMBER);
CREATE OR REPLACE TYPE TYP_TAB_VARCHAR AS TABLE OF TYP_REC_VARCHAR;

Code sql : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
FUNCTION F_LISTING_ZIP (p_doc IN VARCHAR2, p_nomfic IN VARCHAR2 DEFAULT NULL)  
  RETURN TYP_TAB_VARCHAR PIPELINED AS
  zip_files as_zip.file_list;
  v_doc BLOB;
 
  -- p_doc : Doc Zip pour récupérer le blob
  -- p_nomfic : Afficher la liste des noms de fichier contenant p_nomfic
 
BEGIN
/*
drop TYPE TYP_TAB_VARCHAR 
CREATE OR REPLACE TYPE TYP_REC_VARCHAR AS OBJECT (filename VARCHAR2(2000), datemaj DATE, size_comp NUMBER, size_uncomp NUMBER);
CREATE OR REPLACE TYPE TYP_TAB_VARCHAR AS TABLE OF TYP_REC_VARCHAR;
*/
    SELECT blob_zip
    INTO v_doc
    FROM ARCHIVE_FIC
    WHERE nomdoc = p_doc;
 
  zip_files  := as_zip.get_file_list( v_doc);
  IF zip_files IS NOT NULL
  THEN
    FOR i IN zip_files.FIRST .. zip_files.LAST
    LOOP
      IF (p_nomfic IS NOT NULL AND zip_files(i).filename LIKE '%'|| p_nomfic ||'%')
      OR p_nomfic IS NULL
      THEN
        PIPE ROW( TYP_REC_VARCHAR (zip_files(i).filename, zip_files(i).datemaj, zip_files(i).size_comp, zip_files(i).size_uncomp));
      END IF;
    END LOOP;
    END IF;
  RETURN;
END;

5/ Exemples :

zipper un le fichier toto.pdf (présent dans le directory MYDIR) dans le directory MYDIR et s'appelant toto.zip
Code sql : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
DECLARE
  v_zip BLOB;
BEGIN
  AS_ZIP.Add1File(v_zip, 'toto.pdf', AS_ZIP.File2Blob( 'MYDIR', 'toto.pdf' ) );
  AS_ZIP.Finish_Zip(v_zip);
  AS_ZIP.Save_Zip(v_zip, 'MYDIR', 'toto.zip' );
END;

Rajout du fichier toto.txt à mon fichier toto.zip
Code sql : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
DECLARE
  v_zip BLOB;
BEGIN
  v_zip := AS_ZIP.File2Blob( 'MYDIR', 'toto.zip' );
  AS_ZIP.Init_Zip(v_zip);
  AS_ZIP.Add1File(v_zip, 'toto.txt', AS_ZIP.File2Blob( 'MYDIR', 'toto.txt' ) );
  AS_ZIP.Finish_Zip(v_zip);
  AS_ZIP.Save_Zip(v_zip, 'MYDIR', 'toto.zip' );
END;

Dézipper un fichier zip dans un directory Oracle (Attention, les noms de fichier peuvent contenir des répertoires, ce code ne le gère pas)
On affiche le nom de fichier, la date et les tailles.
Code sql : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
DECLARE
  v_zip BLOB;
  zip_files AS_ZIP.file_list;
  v_doc BLOB;
BEGIN
  v_zip := AS_ZIP.File2Blob(p_dir =>'MYDIR', p_file_name => 'toto.zip');
  zip_files  := AS_ZIP.Get_File_List( v_zip );
  FOR i IN zip_files.FIRST() .. zip_files.LAST
  LOOP
        dbms_output.put_line(DBMS_LOB.SUBSTR(zip_files(i).filename,2000,1) ||' '|| TO_CHAR(zip_files(i).datemaj,'DD/MM/RRRR HH24:MI:SS') ||' size:'|| zip_files(i).size_comp ||' / '|| zip_files(i).size_uncomp);
        v_doc := AS_ZIP.Get_File(v_zip, DBMS_LOB.SUBSTR(zip_files(i).filename,2000,1) );
    AS_ZIP.Save_Zip(p_zipped_blob => v_doc, p_dir => 'MYDIR', p_filename => DBMS_LOB.SUBSTR(zip_files(i).filename,2000,1));
  END LOOP;
END;

Toutes les lignes de chargement de fichier zip depuis le serveur [v_zip := AS_ZIP.File2Blob(p_dir =>'MYDIR', p_file_name => 'toto.zip');] peut bien sur être remplacé par un SELECT
Attention toutefois aux limitations sur les BLOB (il faut en faire une copie avant de l'utiliser, à moins de passer par un select for update, mais plus dangereux)
Code sql : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
dbms_lob.createtemporary( v_zip, TRUE );
SELECT BLOB_ZIP INTO v_lob FROM ARCHIVE_FIC WHERE NOMZIP = 'archive_2015';
DBMS_LOB.append(v_zip, v_lob);
AS_ZIP.Init_Zip(v_zip);
AS_ZIP.Add1File(v_zip, ....);
AS_ZIP.Finish_Zip(v_zip);
  ...
dbms_lob.freetemporary(v_zip);

Envoyer le billet « Créer et Modifier un fichier zip en PL/SQL » dans le blog Viadeo Envoyer le billet « Créer et Modifier un fichier zip en PL/SQL » dans le blog Twitter Envoyer le billet « Créer et Modifier un fichier zip en PL/SQL » dans le blog Google Envoyer le billet « Créer et Modifier un fichier zip en PL/SQL » dans le blog Facebook Envoyer le billet « Créer et Modifier un fichier zip en PL/SQL » dans le blog Digg Envoyer le billet « Créer et Modifier un fichier zip en PL/SQL » dans le blog Delicious Envoyer le billet « Créer et Modifier un fichier zip en PL/SQL » dans le blog MySpace Envoyer le billet « Créer et Modifier un fichier zip en PL/SQL » dans le blog Yahoo

Mis à jour 14/09/2015 à 19h39 par McM (Ajout partie 5 : Exemples)

Tags: oracle, plsql, zip
Catégories
Programmation

Commentaires

  1. Avatar de kolodz
    • |
    • permalink
    Bonjour,

    Je viens de regarde le SQL contenu dans le Zip que tu propose. Il y a beaucoup plus de code que ce que je m'attendais à voir !


    Article du blog d'origine sur la création du zip (sauf erreur):
    https://technology.amis.nl/2010/06/0...es-with-plsql/

    Note :
    Peux-tu ajouter le langage aux balises code (=sql) ? Cela facilite la lecture !
  2. Avatar de McM
    • |
    • permalink
    Merci pour le retour. Je ne connaissais pas la notation =sql de la balise Code.

    Si tu vires tous les commentaires, la création d'un zip (add1file et finish_zip et les fonctions de conversion) tient en 200 lignes. Je ne trouve pas ça énorme .. Attends de voir mon code pour la génération d'un QRcode
  3. Avatar de kolodz
    • |
    • permalink
    Citation Envoyé par McM
    Merci pour le retour. Je ne connaissais pas la notation =sql de la balise Code.

    Si tu vires tous les commentaires, la création d'un zip (add1file et finish_zip et les fonctions de conversion) tient en 200 lignes. Je ne trouve pas ça énorme .. Attends de voir mon code pour la génération d'un QRcode
    C'est surtout que je regarde le sujet. Je me dis "Ça va il y a 3 lignes de SQL que je comprends.é Puis, je prend le zip pour vérifier ce qu'il contient... Et là le me rends compte qu'ne fait il y a pas de fonction direct pour gérer les zip (D'où ta source !)