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

Vous êtes nouveau sur Developpez.com ? Créez votre compte ou connectez-vous afin de pouvoir participer !

Vous devez avoir un compte Developpez.com et être connecté pour pouvoir participer aux discussions.

Vous n'avez pas encore de compte Developpez.com ? Créez-en un en quelques instants, c'est entièrement gratuit !

Si vous disposez déjà d'un compte et qu'il est bien activé, connectez-vous à l'aide du formulaire ci-dessous.

Identifiez-vous
Identifiant
Mot de passe
Mot de passe oublié ?
Créer un compte

L'inscription est gratuite et ne vous prendra que quelques instants !

Je m'inscris !

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

Le , par McM

0PARTAGES

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);

Une erreur dans cette actualité ? Signalez-nous-la !