Developpez.com

Télécharger gratuitement le magazine des développeurs, le bimestriel des développeurs avec une sélection des meilleurs tutoriels

Créer et Modifier un fichier zip en PL/SQL
Par McM

Le , par McM, Expert éminent
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é : [ATTACH]184650d1/a/a/a" />

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
SELECT filename, datemaj, size_comp, size_uncomp FROM TABLE (F_LISTING_ZIP(:archives.nomdoc, :q1.nomfic))
Code sql : Sélectionner tout
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
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
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
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
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
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);


Vous avez aimé cette actualité ? Alors partagez-la avec vos amis en cliquant sur les boutons ci-dessous :


 Poster un commentaire

Avatar de kolodz kolodz - Modérateur https://www.developpez.com
le 29/07/2015 à 14:52
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 !
Avatar de McM McM - Expert éminent https://www.developpez.com
le 30/07/2015 à 16:03
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
Avatar de kolodz kolodz - Modérateur https://www.developpez.com
le 30/07/2015 à 16:54
Citation Envoyé par McM;bt1066
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 !)
Offres d'emploi IT
Ingénieur analyste programmeur (H/F)
Safran - Auvergne - Montluçon (03100)
Consultant sap finance/controlling H/F
Safran - Ile de France - Vélizy-Villacoublay (78140)
Architecte et intégrateur scade/simulink H/F
Safran - Ile de France - Vélizy-Villacoublay (78140)

Voir plus d'offres Voir la carte des offres IT
Contacter le responsable de la rubrique Oracle