Module: SpatialFeatures::Unzip
- Defined in:
- lib/spatial_features/unzip.rb
Defined Under Namespace
Classes: PathNotFound
Constant Summary collapse
- IGNORED_ENTRY_PATHS =
paths containing '__macosx' or beginning with a '.'
/(\A|\/)(__macosx|\.)/i.freeze
- NESTED_ARCHIVE_PATTERN =
Archives that may themselves hold the file we're looking for. A KMZ is a ZIP with a KML inside it, and proponents routinely forward a ZIP of per-layer ZIPs rather than the layers themselves.
/\.(zip|kmz)$/i.freeze
- MAX_NESTING_DEPTH =
How many times we'll unwrap a nested archive before giving up. Two levels covers every case we've seen in practice (a ZIP of shapefile ZIPs, a ZIP holding a KMZ) while bounding the work a deeply nested archive can cost us.
2
Class Method Summary collapse
- .entries(file_path) ⇒ Object
- .extract(file_path, tmpdir: nil, downcase: false) ⇒ Object
- .is_zip?(file) ⇒ Boolean
- .matching_paths(paths, find) ⇒ Object
- .names(file_path) ⇒ Object
- .paths(file_path, find: nil, depth: 0, **extract_options) ⇒ Object
-
.paths_in_nested_archives(paths, find:, depth:, tmpdir: nil, **extract_options) ⇒ Object
Unwrap any archives the archive itself contained and search those instead.
Class Method Details
.entries(file_path) ⇒ Object
79 80 81 |
# File 'lib/spatial_features/unzip.rb', line 79 def self.entries(file_path) Zip::File.open(File.path(file_path)) end |
.extract(file_path, tmpdir: nil, downcase: false) ⇒ Object
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
# File 'lib/spatial_features/unzip.rb', line 54 def self.extract(file_path, tmpdir: nil, downcase: false) tmpdir ||= Dir.mktmpdir [].tap do |paths| entries(file_path).each do |entry| next if entry.name =~ IGNORED_ENTRY_PATHS output_filename = entry.name output_filename = output_filename.downcase if downcase path = "#{tmpdir}/#{output_filename}" directory = File.dirname(path) basename = File.basename(path) FileUtils.mkdir_p(directory) entry.extract(basename, destination_directory: directory) paths << path end end end |
.is_zip?(file) ⇒ Boolean
83 84 85 86 87 88 89 |
# File 'lib/spatial_features/unzip.rb', line 83 def self.is_zip?(file) zip = file.readline.start_with?('PK') file.rewind return zip rescue EOFError return false end |
.matching_paths(paths, find) ⇒ Object
33 34 35 |
# File 'lib/spatial_features/unzip.rb', line 33 def self.matching_paths(paths, find) paths.select {|path| find.any? {|pattern| path.index(pattern) } } end |
.names(file_path) ⇒ Object
75 76 77 |
# File 'lib/spatial_features/unzip.rb', line 75 def self.names(file_path) entries(file_path).collect(&:name) end |
.paths(file_path, find: nil, depth: 0, **extract_options) ⇒ Object
18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
# File 'lib/spatial_features/unzip.rb', line 18 def self.paths(file_path, find: nil, depth: 0, **) paths = extract(file_path, **) if find = Array.wrap(find).presence matches = matching_paths(paths, find) # Only unwrap nested archives once the archive has yielded nothing usable, so # archives that already import keep their current behaviour exactly. matches = paths_in_nested_archives(paths, find: find, depth: depth, **) if matches.empty? raise(PathNotFound.new("Archive did not contain a file matching #{find}", paths)) if matches.empty? paths = matches end return Array(paths) end |
.paths_in_nested_archives(paths, find:, depth:, tmpdir: nil, **extract_options) ⇒ Object
Unwrap any archives the archive itself contained and search those instead. Each one
extracts into its own directory so identically named entries (doc.kml in several
KMZs) don't overwrite one another, and so a shapefile's sibling components stay
beside it for Validation.validate_shapefile!.
41 42 43 44 45 46 47 48 49 50 51 52 |
# File 'lib/spatial_features/unzip.rb', line 41 def self.paths_in_nested_archives(paths, find:, depth:, tmpdir: nil, **) return [] unless depth < MAX_NESTING_DEPTH paths.grep(NESTED_ARCHIVE_PATTERN).flat_map do |archive| begin paths(archive, find: find, depth: depth + 1, tmpdir: Dir.mktmpdir(nil, File.dirname(archive)), **) rescue PathNotFound, Zip::Error [] # This nested archive held nothing we can use; the others may still. end end end |