Create ZIP / TAR / .GZ Files in PeopleCode Example

We have been discussing  about manipulating TAR and TAR.GZ files in PeopleSoft at length so far. This discussion would never be complete without providing a tutorial to compress files in ZIP format from PeopleCode. I thought I will write a single code section that will be able to compress a file / set of files in various formats viz tar, tar.gz and zip. It is to be noted that the approach to all these is identical, just that you need to use different class files.  Since our objective is to write a single code, that covers all these formats, we need to reuse functions as much as possible. We will approach this as a step by step guide, steps being shown below:
Generic Code to compress and Create Zip, Tar and Tar.gz files through PeopleCode - Steps
Generic Code to compress and Create Zip, Tar and Tar.gz files through PeopleCode - Steps

1. Identify Files to be Archived


In this step, we create a string array in PeopleCode that will hold the list of files to be archived in required format. For testing purposes, we will push three files into this array, as shown in the code segment below:

Local array of string &FileArray = CreateArrayRept("", 0);
&FileArray.Push("c:\dest.log");
&FileArray.Push("c:\dest.sql");
&FileArray.Push("c:\final.sql");


2. Define Output Stream / Archive Entry Format


The code we are writing can either create a tar file, a zip file or a tar.gz file for the inputs supplied. We use the values 1, 3 and 2 to indicate this respectively. The list of class files we require are shown below: (Note: for TAR file, we use JTAR library as per our earlier tutorial, check references below)


Format
Type of OutputStream
Archive Entry Format
tar
org.xeustechnologies.jtar.TarOutputStream
org.xeustechnologies.jtar.TarEntry
zip
java.util.zip.ZipOutputStream
java.util.zip.ZipEntry
tar.gz
org.xeustechnologies.jtar.TarOutputStream
java.util.zip.GZIPOutputStream
org.xeustechnologies.jtar.TarEntry


We now write an evaluate statement in PeopleCode and set the values suitably. For .gz format, we will define GZIPOutputStream later, as we want to keep the code generic.

Evaluate &desiredOption
When 1 /* Just a TAR file */
When 2 /* TAR.GZ */
   &OutputStreamType = "org.xeustechnologies.jtar.TarOutputStream";
   &ArchiveEntry = "org.xeustechnologies.jtar.TarEntry";
   &finalFileName = "c:\myTarFile.tar";
When 3 /* ZIP file */
   &OutputStreamType = "java.util.zip.ZipOutputStream";
   &ArchiveEntry = "java.util.zip.ZipEntry";
   &finalFileName = "c:\myZipFile.zip";
When-Other
   Error "Wrong Option";
End-Evaluate;



3. Create Archive Entry Function


Zip and TAR files require that you add an entry on each file being added into the archive first. To create this function, we need to supply the following inputs:


  • Archive File – The archive file to which we have to add entries.
  • Name of the Input File – File name for which we have to create the entry
  • Type of Archive –  This parameter will have reference to TarEntry or ZipEntry as a appropriate. At run time, we will create a Java object for this type and add the input file to the archive.
  • Compression Option – Whether to follow TAR (includes .tar.gz) or ZIP option. We need this parameter as the constructor for ZipEntry and TarEntry class takes different arguments.

We use putNextEntry method to add the file to the archive. Once this step is done, we can add the actual bytes to the archive.  The PeopleCode segment for this step is shown below:

Function CreateArchiveEntry(&Archive, &fileNametoAdd, &typeofArchive, &desiredOption)
   /* Create an Archive entry for every file you want to add to the archive */
   Local JavaObject &ArchiveEntry;
   If &desiredOption = 3 Then /* Handle Zip entries differently */
      &ArchiveEntry = CreateJavaObject(&typeofArchive, &fileNametoAdd);
   Else /* Handle TAR and TAR.GZ */
      &ArchiveEntry = CreateJavaObject(&typeofArchive, CreateJavaObject("java.io.File", &fileNametoAdd), &fileNametoAdd);
   End-If;
   /* Add Archive entry to OutputStream */
   &Archive.putNextEntry(&ArchiveEntry);
   /* Call Add Bytes to File method to add actual file to the Archive */
   AddBytesToFile(&Archive, &fileNametoAdd);
End-Function;



4. Create Add Bytes to Archive Function


This function takes the archive and file name. It then reads the input file byte by byte and adds it to the archive. This method is generic across zip, tar and tar.gz formats. We have discussed this function in depth in our earlier tutorials, so we just provide the code for this function.

Function AddBytesToFile(&archive, &fileName)
   /* Read source file into input stream */
   Local JavaObject &in = CreateJavaObject("java.io.FileInputStream", &fileName);
   /* Java Array that will read bytes from input file */
   Local JavaObject &filebuffer = CreateJavaArray("byte[]", 1024);
   Local number &byteCount = &in.read(&filebuffer);
   /* Read bytes from input file and load it byte array  - Do until all bytes are read*/
   While &byteCount > 0
      /* Write Bytes of Data to corresponding Archive Output Stream */
      &archive.write(&filebuffer, 0, &byteCount);
      &byteCount = &in.read(&filebuffer);
   End-While;
   /* Close input stream */
   &in.close();
End-Function;



5. Handle .gz File Compression


For tar.gz files alone, we have to create a .gz file after creating the TAR file. To do this, we create an object of type GZIPOutputStream and read the TAR file created earlier to this stream to achieve the compression. The same add bytes method can be reused for this purpose. The PeopleCode segment is shown below:

/* We will now create a GZIP file only for Option 2*/
If &desiredOption = 2 Then
   Local JavaObject &gzip = CreateJavaObject("java.util.zip.GZIPOutputStream", CreateJavaObject("java.io.FileOutputStream", "c:\myTarFile.tar.gz", True));
   AddBytesToFile(&gzip, "c:\myTarFile.tar");
   &gzip.close();
End-If;


File Compression  - Create ZIP / TAR / TAR.GZ Files in PeopleCode



The complete PeopleCode example to compress files and create zip / tar / tar.gz output is shown below:

Function AddBytesToFile(&archive, &fileName)
   /* Read source file into input stream */
   Local JavaObject &in = CreateJavaObject("java.io.FileInputStream", &fileName);
   /* Java Array that will read bytes from input file */
   Local JavaObject &filebuffer = CreateJavaArray("byte[]", 1024);
   Local number &byteCount = &in.read(&filebuffer);
   /* Read bytes from input file and load it byte array  - Do until all bytes are read*/
   While &byteCount > 0
      /* Write Bytes of Data to corresponding Archive Output Stream */
      &archive.write(&filebuffer, 0, &byteCount);
      &byteCount = &in.read(&filebuffer);
   End-While;
   /* Close input stream */
   &in.close();
End-Function;
Function CreateArchiveEntry(&Archive, &fileNametoAdd, &typeofArchive, &desiredOption)
   /* Create an Archive entry for every file you want to add to the archive */
   Local JavaObject &ArchiveEntry;
   If &desiredOption = 3 Then /* Handle Zip entries differently */
      &ArchiveEntry = CreateJavaObject(&typeofArchive, &fileNametoAdd);
   Else /* Handle TAR and TAR.GZ */
      &ArchiveEntry = CreateJavaObject(&typeofArchive, CreateJavaObject("java.io.File", &fileNametoAdd), &fileNametoAdd);
   End-If;
   /* Add Archive entry to OutputStream */
   &Archive.putNextEntry(&ArchiveEntry);
   /* Call Add Bytes to File method to add actual file to the Archive */
   AddBytesToFile(&Archive, &fileNametoAdd);
End-Function;

/* PeopleCode - Create ZIP / TAR / TAR.GZ Archive - Common Example
You can use this code to

1- TAR Single or multiple files
2 - TAR.GZ Single or Multiple Files
3- ZIP Single or Multiple Files */

Local array of string &FileArray = CreateArrayRept("", 0);
&FileArray.Push("c:\dest.log");
&FileArray.Push("c:\dest.sql");
&FileArray.Push("c:\final.sql");

/* This value defines the final output */
Local number &desiredOption = 3;
Local string &finalFileName, &ArchiveEntry, &OutputStreamType;
Evaluate &desiredOption
When 1 /* Just a TAR file */
When 2 /* TAR.GZ */
   &OutputStreamType = "org.xeustechnologies.jtar.TarOutputStream";
   &ArchiveEntry = "org.xeustechnologies.jtar.TarEntry";
   &finalFileName = "c:\myTarFile.tar";
When 3 /* ZIP file */
   &OutputStreamType = "java.util.zip.ZipOutputStream";
   &ArchiveEntry = "java.util.zip.ZipEntry";
   &finalFileName = "c:\myZipFile.zip";
When-Other
   Error "Wrong Option";
End-Evaluate;
/* Create Archive Object */
Local JavaObject &myArchive = CreateJavaObject(&OutputStreamType, CreateJavaObject("java.io.FileOutputStream", &finalFileName, True));
For &i = 1 To &FileArray.Len
   CreateArchiveEntry(&myArchive, &FileArray [&i], &ArchiveEntry, &desiredOption);
End-For;
/* Close output Stream */
&myArchive.close();
/* We will now create a GZIP file only for Option 2*/
If &desiredOption = 2 Then
   Local JavaObject &gzip = CreateJavaObject("java.util.zip.GZIPOutputStream", CreateJavaObject("java.io.FileOutputStream", "c:\myTarFile.tar.gz", True));
   AddBytesToFile(&gzip, "c:\myTarFile.tar");
   &gzip.close();
End-If;



6. Test zip/ tar/ tar.gz output


This program is tested with different input options for the same set of input files. The output files produced by the program in each of these cases are shown below:

Output TAR, ZIP and TAR.GZ Files Created in PeopleCode
Output TAR, ZIP and TAR.GZ Files Created in PeopleCode

We have now successfully created a generic PeopleCode implementation to create tar / zip / tar.gz files .

We have now completed the compression part of this tutorial series. The next step would be to define Peoplecode to extract TAR, GZ and ZIP files. We will discuss that in the upcoming posts. Meanwhile, you should checkout the references section of this tutorial that will have detailed technical information on some of these approaches.

References:

4 comments:

  1. There is a way to eliminate the path of the files inside the zip? I have a lot of files *.txt in the path: \PeopleSoft\Dir1\dir2\Dir3 and I only want to archive/créate the zip file but not store the full path of the txt files.

    ReplyDelete
    Replies
    1. Hi - were you able to resolve the eliminate the extra folders that come when you unzip it?

      Delete
  2. when you create your "ZipEntry", create with only the file name, not with full file path. Reference full file path only for "FileInputStream".

    ReplyDelete
  3. Hi,

    I am able to compress the file using above code. However, it truncates last 1 KB of data from the file in zipped folder.

    Is it something to do with buffer size:
    Local JavaObject &filebuffer = CreateJavaArray("byte[]", 1024);

    Regards,
    Swati

    ReplyDelete