# # File:: %RS_TOOLSLIB%/pipeline/projectutil/data_zip.rb # Description:: Zip file functions. # # Author:: David Muir # Date:: 24 June 2011 # #----------------------------------------------------------------------------- # Uses #----------------------------------------------------------------------------- require 'pipeline/log/log' require 'pipeline/os/path' include Pipeline require 'time' require 'zip/zip' include Zip require 'pipeline/util/rubyzip2_mtime_fix' #----------------------------------------------------------------------------- # Implementation #----------------------------------------------------------------------------- module Pipeline module ProjectUtil @@zip_log = Pipeline::Log::new( 'data_zip' ) # # == Description # Extract the content of a ZIP file to a destination directory. The # optional block is executed for each file that is extracted specifying the # full path of the extracted file. # # ZIP sub-directories are preserved in the destination directory. # # The filenames of the extracted files are return in an Array. # # == Example Usage # See %RS_TOOLSLIB%/util/data_extract_zip.rb. # def ProjectUtil::data_zip_extract( filename, destination, overwrite = false, filter = '*.*', &block ) dest_files = [] FileUtils::mkdir_p( destination ) unless ( File::directory?( destination ) ) Dir::chdir( destination ) do ZipFile::open( filename ) do |zf| zf.each do |entry| # Skip filenames that do not match filter. next unless ::File::fnmatch( filter, entry.name, File::FNM_CASEFOLD ) dir = OS::Path::get_directory( entry.name ) FileUtils::mkdir_p( dir ) unless ( dir.nil? or 0 == dir.size or File::directory?( dir ) ) entry.extract do overwrite end # Local time. dest_file = OS::Path::combine( destination, entry.name ) mtime = ( entry.time ) File::utime( mtime, mtime, dest_file ) dest_files << dest_file yield( dest_file ) if ( block_given? ) end end end dest_files end # # == Description # Extract the contents of a ZIP file to a destination directory. The # block is executed with each ZipEntry object as a parameter; if the block # returns true then the file is extracted, otherwise the file is not extracted. # If the 'overwrite' parameter is used, the block still needs to evaluate # to true for the file to be written. # # ZIP sub-directories are preserved in the destination directory. # # The filenames of the extracted files are return in an Array. # # == Example Usage # DHM TODO # def ProjectUtil::data_zip_extract2( filename, destination, overwrite = false, filter = '*.*', &block ) dest_files = [] FileUtils::mkdir_p( destination ) unless ( File::directory?( destination ) ) Dir::chdir( destination ) do ZipFile::open( filename ) do |zf| zf.each do |entry| # Skip filenames that do not match filter. next unless ::File::fnmatch( filter, entry.name, File::FNM_CASEFOLD ) dir = OS::Path::get_directory( entry.name ) FileUtils::mkdir_p( dir ) unless ( dir.nil? or 0 == dir.size or File::directory?( dir ) ) if ( block_given? and ( yield entry ) ) then entry.extract do overwrite end else entry.extract do overwrite end end # Local time. dest_file = OS::Path::combine( destination, entry.name ) mtime = ( entry.time ) File::utime( mtime, mtime, dest_file ) dest_files << dest_file end end end dest_files end # # == Description # Extract the contents of a ZIP file to a destination directory. Only # files newer than the destination files are overwritten; this can be forced # by setting 'overwrite' to true. # # ZIP sub-directories are preserved in the destination directory. # # The filenames of the extracted files are return in an Array. # # == Example Usage # DHM TODO # def ProjectUtil::data_zip_extract3( filename, destination, overwrite = false, filter = '*.*', &block ) ProjectUtil::data_zip_extract2( filename, destination, overwrite, filter ) do |entry| destination_file = OS::Path::combine( destination, entry.name ) if ( File::exists?( destination_file ) ) then # Destination file exists; extract if its modified date is # earlier than the modified date of the zip entry. mtime = File::mtime( destination_file ) ( ( mtime <=> entry.time ) < 0 ) else # Destination file does not exist so we should extract. true end end end # # == Description # Return Array of filenames within the zip file. Also yields each # filename to the block, if given. # def ProjectUtil::data_zip_filelist( filename, &block ) dest_files = [] ZipFile::foreach( filename ) do |zf| yield( zf.name ) if ( block_given? ) dest_files << zf.name end dest_files end # # == Description # Create a ZIP file; from the specific file list. # # file_list is an Array; of either file path Strings or Hash objects # containing two keys, :src, :dst specifying source file and destination # path in the zip file. # # User-block is yielded for each file added (with filename as parameter). # # == Example Usage # See %RS_TOOLSLIB%/util/data_mk_generic_zip.rb # def ProjectUtil::data_zip_create( filename, file_list, compress = true, &block ) directory = OS::Path::get_directory( filename ) FileUtils::mkdir_p( directory ) if ( not File::directory?( directory ) ) # Delete the existing Zip file if it exists. FileUtils::rm( filename ) if ( File::exists?( filename ) ) # Create the Zip file and write our entries. ZipFile::open( filename, ZipFile::CREATE ) do |zf| file_list.each do |path| if ( path.is_a?( String ) and File::file?( path ) ) then if ( not File::exists?( path ) ) then @@zip_log.error( "File #{path} does not exist. Skipping." ) next end entry = zf.add( OS::Path::get_filename( path ), path ) do true # continue on exists (overwrite) end entry.time = File::mtime( File::expand_path( path ) ).getlocal entry.compression_method = ZipEntry::DEFLATED if ( compress ) entry.compression_method = ZipEntry::STORED unless ( compress ) yield( path ) if ( block_given? ) elsif ( path.is_a?( Hash ) and path.has_key?( :src ) and path.has_key?( :dst ) ) if ( not File::exists?( path[:src] ) ) then @@zip_log.error( "File #{path} does not exist. Skipping." ) next end entry = zf.add( path[:dst], path[:src] ) do true # continue on exists (overwrite) end entry.time = File::mtime( File::expand_path( path[:src] ) ).getlocal entry.compression_method = ZipEntry::DEFLATED if ( compress ) entry.compression_method = ZipEntry::STORED unless ( compress ) yield( path ) if ( block_given? ) else @@zip_log.error( "Misformed entry in file_list (#{path.class}, '#{path.to_s}')." ) end end zf.comment = "Created by #{__FILE__} on #{DateTime::now}" @@zip_log.info( "Zip file #{filename} created (#{file_list.size} entries)." ) zf.close() end true end # # == Description # Read the contents of a file into memory and return its content as a # String. # def ProjectUtil::data_zip_readfile( filename, contained_filename ) data = nil ZipFile::open( filename ) do |zf| data = zf.read( contained_filename ) end end end # ProjectUtil module end # Pipeline module # %RS_TOOLSLIB%/pipeline/projectutil/data_zip.rb