How can I expand only a particular subdirectory of a zip(Tree)?

How can I expand contents from a zipTree in a way that only the contents of a given sub directory gets extracted

2 Likes

Use an include filter:

task unzip(type: Copy) {
  from zipTree(zipFile)
  into "unzipped"
  include "dir/to/unzip/**"
}

Hi Peter thank you this has but this does unzipā€™s but it extracts the all directory tree and not the contents from that given subdirectory. Say if the contents of the zip where: /Aā€”+

|

Bā€”C

|

D----E

|-----F If the include is ā€œA/Dā€, I was expecting to have only E F In the output

If the include is ā€œA/Dā€, I was expecting to have only E F In the output

Thatā€™s not how include works. Ideally, you could achieve your goal with rename. However, rename currently operates on file names rather than file paths. What you can do is to add another copy task/action that copies everything below A/D to a new place. Or, code a solution based on zipTree(zipFile).visit { ā€¦ }.

Thank you, Peter

Vote++ for a ā€œrenameā€ variant that can operate on full file paths :wink:

You can use the ā€˜eachFile {}ā€™ hook to do this.

task unzip(type: Copy) {
  from zipTree(zipFile)
  into "unzipped"
  eachFile { FileCopyDetails fcp ->
    if (fcp.relativePath.pathString.startsWith("dir/to/unzip/")) {
      // remap the file to the root
      fcp.relativePath = new RelativePath(fcp.file, fcp.relativePath.segments[3..-1])
    } else {
      fcp.exclude()
    }
  }
}

Thatā€™s untested, but it should be pretty close.

docs:

http://www.gradle.org/docs/current/javadoc/org/gradle/api/file/RelativePath.html http://gradle.org/docs/current/dsl/org.gradle.api.tasks.Copy.html#org.gradle.api.tasks.Copy:eachFile(groovy.lang.Closure)

1 Like

@Luke it is necessary to coerce the segments back into a String Array after removing elements. The code below works but has the unintended side effect of also copying the original directory structure with no files in it. Any thoughts?

eachFile { FileCopyDetails fcp ->
            if (fcp.relativePath.pathString.startsWith(appName)) {
              // remap the file to the root
              def segments = fcp.relativePath.segments
              def pathsegments =segments[1..-1] as String[]
              fcp.relativePath = new RelativePath(!fcp.file.isDirectory(), pathsegments)
            }
            else {
              fcp.exclude()
            }
        }

You need to specify to ignore empty dirs:

task unzip(type: Copy) {
  ā€¦
  includeEmptyDirs = false
}

We actually do have directories in the structure that are empty that we would like to unzip. Any Ideas?

This is a limitation of the API. You could keep track of what all the directories will be in the eachFile, and use a ā€˜doLast { project.delete ā€œremapped dirā€ }ā€™.

Iā€™m hitting this same issue. Utility could be improved if the zipTree method took argument for a directory within the archive from which to root the tree. For example:

task unzip(type: Copy) {
   from zipTree(zipFile, root: 'directory/in/the/zip/to/consider/as/root')
   into destDir
   include 'paths/relative/to/root'
}

@Ryan, that seems like a good solution to a common problem. Raised GRADLE-3025 to track it.

Ant to the rescue! https://ant.apache.org/manual/Tasks/unzip.html

task unzip(){
  ant.unzip(src: 'temp/test.war', dest:'lib', overwrite:"true") {
    patternset( ) {
      include( name: 'WEB-INF/lib/*.jar' )
    }
    mapper(type:"flatten")
  }
}

Although the Copy task with the zipTree sort of makes sense, it isnā€™t the first place you look when trying to unzip something. Why isnā€™t there a wrapper task called Unzip?

+1 for GRADLE-3025.

Iā€™m trying to combine the output of several subprojects with ā€˜applicationā€™ plugin into one ā€œmasterā€ ā€˜distZipā€™. Not being able to select the content in the subdirectories of the child distZips direclty is making me put a lot of effort into itā€¦