CGI/Perl Guide | Learning Center | Forums | Advertise | Login
Site Search: in

  Main Index MAIN
INDEX
Search Posts SEARCH
POSTS
Who's Online WHO'S
ONLINE
Log in LOG
IN

Home: Perl Programming Help: Beginner:
Problem using Archive::Zip. n00b looking for assistance

 



timark
Novice

Jun 29, 2011, 3:38 AM

Post #1 of 16 (2326 views)
Problem using Archive::Zip. n00b looking for assistance Can't Post

Hi Everyone,

I am a Perl n00b. Having learned a bit by myself, I am now trying to target small scripts to build my skills.

My current project is to write a script for performing file archives. The aim is to enter a directory zip all sub-directories and files with <their_names>.zip respectively.

As of now, I have been able to do a directory traversal. I have also managed to enter a main directory and use addTree to create an archive of all files and directories and write it to a .zip file in that main directory.

But when I tried what I exactly intend to do (as described in para 2) I am unable to create the .zip or write it. Could you kindly give me any guidance in this matter.

I am posting the code below. Any help is appreciated. Kindly pardon any errors.

Thanks guys!

Note: A few variables may be repetitive because I was trying out the codes and make them work as separate modules and then tried integrating them.

Code
use warnings; 
use Archive::Zip qw(:ERROR_CODES :CONSTANTS);
use Cwd;

my $dir_path = "D:/Strawberry/perlcodes/Dummy_Project/";
my $dir = "C:/Strawberry/perlcodes/Dummy_Project/";
print "Hello! \n \n"; #sample print for checking if all is okay :P

opendir(my $dir_handle, $dir_path) || die "can't opendir $dir_path: $!"; #try open path in $dir_path and pass handle to $dir_handle, if it does not work give the error msg
my @files = readdir($dir_handle);
print"Current Path is: $dir_path\n"; #print current path which was given to $dir_path in beginning
print"List of directories in this path: \n";
my $zipName = "Inside_Project.zip";
my $pwd = cwd();
chdir($dir);

foreach (@files){ #traversing through all elements of array i.e. elements in the directory path given

if (-d $_){ #performing test to check if the particular element is a directory
#$dir_path.$_ is done to perform the test with the exact current file in that path else it performs test with th ecurrent working directory and will fail all the tests
unless (my $files = m/^\.+/){ #perform test via regex to eliminate all directories that start with the (.) symbol
print ("$_\n"); #print the element i.e. directory name
my $zip = Archive::Zip->new();
$zip->addTreeMatching("/$_", 'NAME', sub { -f && -r && -d}) or warn "Can't add tree $_\n";
print "Tree added to $_\n";
$zip->writeToFileNamed("$_") or warn "Could not zip file $_.zip";
print "Zip File $_.zip written\n";
}
}
}
chdir($pwd);
closedir $dir_handle;



rovf
Veteran

Jun 29, 2011, 7:51 AM

Post #2 of 16 (2322 views)
Re: [timark] Problem using Archive::Zip. n00b looking for assistance [In reply to] Can't Post


Quote
I am unable to create the .zip or write it.


(1) You have posted your code, which is a good thing. However you should in any case add

use strict;

and re-run your code.

(2) You don't say whether you get any (error-) messages.

(3) In your error handling, you only detect the fact *that* the Zip-routine returns an error, but you don't display *what* error occured. See the ERROR CODES section in the documentation of Archive.:Zip.


timark
Novice

Jun 29, 2011, 1:52 PM

Post #3 of 16 (2317 views)
Re: [rovf] Problem using Archive::Zip. n00b looking for assistance [In reply to] Can't Post

Hi rofv,

Thanks for pointing them out. I have made the following modifications in my code.

Code
use strict; 
use warnings;
use Archive::Zip qw(:ERROR_CODES :CONSTANTS);
use Cwd;

my $dir_path = "C:/Strawberry/perlcodes/Dummy_Project/";
my $dir = "C:/Strawberry/perlcodes/Dummy_Project/";
print "Hello! \n \n"; #sample print for checking if all is okay :P

opendir(my $dir_handle, $dir_path) || die "can't opendir $dir_path: $!"; #try open path in $dir_path and pass handle to $dir_handle, if it does not work give the error msg
my @files = readdir($dir_handle);
print"Current Path is: $dir_path\n"; #print current path which was given to $dir_path in beginning
print"List of directories in this path: \n";
my $zipName = "Inside_Project.zip";
my $pwd = cwd();
chdir($dir);

foreach (@files){ #traversing through all elements of array i.e. elements in the directory path given

if (-d $_){ #performing test to check if the particular element is a directory
#$dir_path.$_ is done to perform the test with the exact current file in that path else it performs test with th ecurrent working directory and will fail all the tests
unless (my $files = m/^\.+/){ #perform test via regex to eliminate all directories that start with the (.) symbol
print ("$_\n"); #print the element i.e. directory name
my $zip;
if (($zip = Archive::Zip->new()) != AZ_OK){
print "Error in creating new archive. Error: $!\n";
}else {
print "Archive created successfully\n";
}
if ($zip->addTreeMatching("/$_", 'NAME', sub { -f && -r && -d}) != AZ_OK){
print "Can't add tree $_ Error: $!\n";
}else{
print "Tree added successfully to $_\n";
}
if($zip->writeToFileNamed("$_.zip") != AZ_OK){
print "Could not zip file $_.zip\n Error: $!\n";
}else{
print "Zip File $_.zip written\n";
}
}
}
}


chdir($pwd);
closedir $dir_handle;


The output on the terminal is:

Code
 
C:\Strawberry\perlcodes>perl tree.pl
Hello!

Current Path is: C:/Strawberry/perlcodes/Dummy_Project/
List of directories in this path:
One_More
Error in creating new archive. Error: No such file or directory
Tree added successfully to One_More
Zip File One_More.zip written
Test_Folder
Error in creating new archive. Error: Inappropriate I/O control operation
Tree added successfully to Test_Folder
Zip File Test_Folder.zip written

C:\Strawberry\perlcodes>



rovf
Veteran

Jun 30, 2011, 1:41 AM

Post #4 of 16 (2305 views)
Re: [timark] Problem using Archive::Zip. n00b looking for assistance [In reply to] Can't Post

You are evaluating the error by displaying $!. I didn't check the code, but from the documention (I am using version 1.20 of Archive::Zip), I see no hint that $! has a defined value (i.e. the value of $! can or can not be an indication about the failure). I certainly would not rely on $! for error diagnostics here...


timark
Novice

Jun 30, 2011, 2:02 AM

Post #5 of 16 (2302 views)
Re: [rovf] Problem using Archive::Zip. n00b looking for assistance [In reply to] Can't Post

Thanks rovf. But I am unable to find any other way fo displaying the error. Could you point me in the right direction please.

I tried this mod after searching, but am not sure if its sufficient. The error does not give me any info even after searching.


Code
use strict; 
use warnings;
use Archive::Zip qw(:ERROR_CODES :CONSTANTS);
use Cwd;

my $dir_path = "C:/Strawberry/perlcodes/Dummy_Project/";
my $dir = "C:/Strawberry/perlcodes/Dummy_Project/";
print "Hello! \n \n"; #sample print for checking if all is okay :P

opendir(my $dir_handle, $dir_path) || die "can't opendir $dir_path: $!"; #try open path in $dir_path and pass handle to $dir_handle, if it does not work give the error msg
my @files = readdir($dir_handle);
print"Current Path is: $dir_path\n"; #print current path which was given to $dir_path in beginning
print"List of directories in this path: \n";
my $zipName = "Inside_Project.zip";
my $pwd = cwd();
chdir($dir);

foreach (@files){ #traversing through all elements of array i.e. elements in the directory path given

if (-d $_){ #performing test to check if the particular element is a directory
#$dir_path.$_ is done to perform the test with the exact current file in that path else it performs test with th ecurrent working directory and will fail all the tests
unless (my $files = m/^\.+/){ #perform test via regex to eliminate all directories that start with the (.) symbol
print ("$_\n"); #print the element i.e. directory name
my $status;
my $zip;
if( ($status = ($zip = Archive::Zip->new())) != AZ_OK){
print "Error in creating new archive. Error: $!\n$status\n";
}else {
print "Archive created successfully\n";
}
if (($status= $zip->addTreeMatching("/$_", 'NAME', sub { -f && -r && -d})) != AZ_OK){
print "Can't add tree $_ Error: $!\n$status\n";
}else{
print "Tree added successfully to $_\n";
}
if(($status= $zip->writeToFileNamed("$_.zip")) != AZ_OK){
print "Could not zip file $_.zip\n Error: $!\n$status\n";
}else{
print "Zip File $_.zip written\n";
}
}
}
}

chdir($pwd);
closedir $dir_handle;


terminal:

Code
Hello! 

Current Path is: C:/Strawberry/perlcodes/Dummy_Project/
List of directories in this path:
One_More
Error in creating new archive. Error: No such file or directory
Archive::Zip::Archive=HASH(0x2764354)
Tree added successfully to One_More
Zip File One_More.zip written
Test_Folder
Error in creating new archive. Error: Inappropriate I/O control operation
Archive::Zip::Archive=HASH(0x69bed4)
Tree added successfully to Test_Folder
Zip File Test_Folder.zip written



rovf
Veteran

Jun 30, 2011, 2:03 AM

Post #6 of 16 (2301 views)
Re: [timark] Problem using Archive::Zip. n00b looking for assistance [In reply to] Can't Post

One more thing: The condition

($zip = Archive::Zip->new()) != AZ_OK

does not make sense. new either returns a reference to a Archive::Zip object, or undef.


timark
Novice

Jun 30, 2011, 2:15 AM

Post #7 of 16 (2300 views)
Re: [rovf] Problem using Archive::Zip. n00b looking for assistance [In reply to] Can't Post

But I have not seen any code with using an error test for $zip = Archive::Zip->new()
I tried the same code without the condition check. There are no error messages, and like before, the code runs and creates 2 zip archives but they are empty! No difference even after removing this condition.


rovf
Veteran

Jun 30, 2011, 4:25 AM

Post #8 of 16 (2297 views)
Re: [timark] Problem using Archive::Zip. n00b looking for assistance [In reply to] Can't Post


Quote
But I am unable to find any other way fo displaying the error. Could you point me in the right direction please.


I already did, in my post #2 in this thread, section (3).


rovf
Veteran

Jun 30, 2011, 4:53 AM

Post #9 of 16 (2295 views)
Re: [timark] Problem using Archive::Zip. n00b looking for assistance [In reply to] Can't Post

It is true that the documentation does not say anything about signalling an error within new() via the returned value. It is just good practice to check the returned reference for being defined.


Quote
the code runs and creates 2 zip archives but they are empty


Well, this is the FIRST time you are mentioning this, and in fact in contradicts your very first posting, where you had written:


Quote
I am unable to create the .zip or write it.


So, in your first posting, you claimed that you could not create the zip file, while in your last posting, you said you could create it (though it is empty)!

How can one reasonably help you, if you don't even describe the problem in an understandable way????

I suggest you go back to square one: Describe exactly your problem (i.e. what you get, as opposed to what you want to get), post your code (with error checking!), and then we can try to find out what is going wrong....


timark
Novice

Jul 8, 2011, 3:15 AM

Post #10 of 16 (2261 views)
Re: [rovf] Problem using Archive::Zip. n00b looking for assistance [In reply to] Can't Post

Thanks rovf for the assistance. I was able to make the code work. The problem was with the path that I was seeking to add the tree. Since it was starting with a "/" I guess it was checking in root for the tree to add. I removed the "/" and used only "$_" and it worked perfectly.

Coming to the error catching. I really did not understand how to catch it for the

Code
my $zip = Archive::Zip->new();

because its return is undef if it fails. So I can only check if it returns undef and print a failure. For the other zip functions, I think I pretty much got it right, or did I not?

Please do correct me. I do not have much of a background in programming and am totally new to Perl.


rovf
Veteran

Jul 8, 2011, 3:33 AM

Post #11 of 16 (2258 views)
Re: [timark] Problem using Archive::Zip. n00b looking for assistance [In reply to] Can't Post

Though it does not harm to check new() for the result of undef, in the case of Archive, I guess this won't ever happen (but you should have a look at the code to be sure). However, you should do proper error checking after each method call of your $zip object.


rovf
Veteran

Jul 8, 2011, 3:38 AM

Post #12 of 16 (2257 views)
Re: [timark] Problem using Archive::Zip. n00b looking for assistance [In reply to] Can't Post


Quote
I do not have much of a background in programming and am totally new to Perl.


Sorry, overlooked that note. In this case, you probably don't know the following:

(1) To look at the documentation of the Archive module, enter on the command line

perldoc Archive::Zip

(2) To look at the actual code of the Archive module, enter on the command line

perldoc -M Archive::Zip

and search for the definition of sub new.

In most cases, the constructors of the class (new) are written in a way that they always return something defined, but there are exceptions (IO::File for example). Hence it is a good practice to always check the return value of new() whether it is defined or not. However, in the case of Archive::Zip, new() doesn't do any I/O operation (after all, it doesn't know where to write to), so even without looking at the code of new(), it is extremely unlikely that new() would ever return undef.


timark
Novice

Jul 8, 2011, 3:50 AM

Post #13 of 16 (2256 views)
Re: [rovf] Problem using Archive::Zip. n00b looking for assistance [In reply to] Can't Post

Thanks for the detailed explanation rovf. I shall have a look at the code and documentation as you mentioned.

For now, this seems to be working perfectly. (No checking for the new() function)

Code
sub ziptree { 
my @objs = @_;
print"\nBeginning Archive of Directories in this path.\n";
foreach (@objs){
if(-d $_){
unless (m/^\.+/){
my $zip = Archive::Zip->new();
my $status= $zip->addTree("$_", "$_");
if ($status != AZ_OK){
print "Can't add tree $_ Error: $!\n$status\n";
}else{
print "Tree added successfully to $_\n";
}
$status= $zip->writeToFileNamed("$_.zip");
if($status != AZ_OK){
print "Could not zip file $_.zip\n Error: $!\n$status\n";
}else{
print "Zip File $_.zip written\n";
}
}
}
}

}

The subroutine call is passed an array with a list of entities in that directory.


rovf
Veteran

Jul 8, 2011, 4:33 AM

Post #14 of 16 (2254 views)
Re: [timark] Problem using Archive::Zip. n00b looking for assistance [In reply to] Can't Post

Yes, but you still display $! in case of error, which is questionable (at least I wouldn't rely to much on it, since according to the docs, its value is not defined):


Code
if ($status != AZ_OK){  
print "Can't add tree $_ Error: $!\n$status\n";
}else{


I also had mentioned this in my post #4 in this thread!

Also, you just output $status. I wonder what information you will get if you just know that $status is 3!

I admit that the error signaling implemented in Archive::Zip needs some improvement, but at least you should map the error codes to some sensible text.

Did you REALLY check what the docs say about error handling?


timark
Novice

Jul 8, 2011, 4:46 AM

Post #15 of 16 (2252 views)
Re: [rovf] Problem using Archive::Zip. n00b looking for assistance [In reply to] Can't Post

I did read the CPAN documentation on the errors, but I could not make much sense of it. The different types of AZ_OK are ennumerated and what for each type of error, but I cant seem to comprehend much from it. In case of an error it ends up printing a "HASH<some_number_here>" when I use $status to print the error.

So, I need to check the code and documentation as you mentioned in your previous post. I will do that next.

From what little Perl I have learned, I had noticed $! being used to print the errors. That is why I was using it initially and after you expressed your doubts on it I changed to the $status variable.


rovf
Veteran

Jul 8, 2011, 5:13 AM

Post #16 of 16 (2251 views)
Re: [timark] Problem using Archive::Zip. n00b looking for assistance [In reply to] Can't Post


Quote
The different types of AZ_OK are ennumerated and what for each type of error, but I cant seem to comprehend much from it.


Yes, the design is terrible. You would need something like:


Code
my $ziperrors = { 
(AZ_STREAM_END) => 'last chunk read',
(AZ_IO_ERROR) => 'I/O error',
...
}


(Note that you need the parentheses)

However, you *can* also get at a reasonable text: See the documentation for the function setErrorHandler, which allows you to set a callback. This callback is called with a human-readable (hooray) string describing the error (though I had to actually read the code to find out what's going on).

You can define an error handler like this:


Code
my $archive_error_string; 
$zip->setErrorHandler(
sub {
$archive_error_string=$_[0]
}
);


and then find in case of an error the error message on $archive_error_string.


$zip->setErrorHandler(

 
 


Search for (options) Powered by Gossamer Forum v.1.2.0

Web Applications & Managed Hosting Powered by Gossamer Threads
Visit our Mailing List Archives