par , 28/07/2015 à 19h34 (7425 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
SELECT filename, datemaj, size_comp, size_uncomp FROM TABLE (F_LISTING_ZIP(:archives.nomdoc, :q1.nomfic))
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; |
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
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
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.
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)
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); |
Mis à jour 14/09/2015 à 19h39 par McM
(Ajout partie 5 : Exemples)
- Catégories
-
Programmation