Gradle - 从URL下载并解压文件

46

如何使用Gradle从URL(http)下载并解压文件?如果可能的话,我想避免每次运行任务都重新下载(在ant.get中可以通过skipexisting:'true'来实现)。我当前的解决方案是:

task foo {
  ant.get(src: 'http://.../file.zip', dest: 'somedir', skipexisting: 'true')
  ant.unzip(src: 'somedir' + '/file.zip', dest: 'unpackdir')
}

不过,我希望能够找到一个无蚂蚁的解决方案。有没有可能实现呢?


2
不要忘记使用 doLast { ... } 包装任务的执行部分(与您之前的问题中一样的错误)。 - Peter Niederwieser
1
【悬赏】有人能提供下面答案的示例吗:“如果你想从Gradle的依赖解析/缓存功能中受益,可以通过假装它是一个带有自定义构件URL的Ivy仓库来实现”? - CMPS
Netflix发布了一些插件:https://github.com/nebula-plugins/nebula-core - 不确定为什么这不再维护。 - koppor
7个回答

94

假设您想将此zip文件作为依赖项下载:

https://github.com/jmeter-gradle-plugin/jmeter-gradle-plugin/archive/1.0.3.zip
你将你的ivy仓库定义为:

You define your ivy repo as:

repositories {
    ivy {
        url 'https://github.com/'

        patternLayout {
            artifact '/[organisation]/[module]/archive/[revision].[ext]'
        }

        // This is required in Gradle 6.0+ as metadata file (ivy.xml) 
        // is mandatory. Docs linked below this code section
        metadataSources { artifact() } 
    }
}

所需元数据的参考在此处

然后可以使用该依赖项:

dependencies {
    compile 'jmeter-gradle-plugin:jmeter-gradle-plugin:1.0.3@zip'
    //This maps to the pattern: [organisation]:[module]:[revision]:[classifier]@[ext]         
}

解压缩:

task unzip(type: Copy) {

  def zipPath = project.configurations.compile.find {it.name.startsWith("jmeter") }
  println zipPath
  def zipFile = file(zipPath)
  def outputDir = file("${buildDir}/unpacked/dist")

  from zipTree(zipFile)
  into outputDir

}

可选:

如果您的项目中有多个存储库,使用相关存储库限制依赖项搜索可能有助于构建时间和某种程度的安全性。

Gradle 6.2+:

repositories {
    mavenCentral()
    def github = ivy {
        url 'https://github.com/'
        patternLayout {
            artifact '/[organisation]/[module]/archive/[revision].[ext]'
        }
        metadataSources { artifact() }
    }
    exclusiveContent {
        forRepositories(github)
        filter { includeGroup("jmeter-gradle-plugin") }
    }
}

早期版本的Gradle:

repositories {
    mavenCentral {
        content { excludeGroup("jmeter-gradle-plugin") }
    }
    ivy {
        url 'https://github.com/'
        patternLayout {
            artifact '/[organisation]/[module]/archive/[revision].[ext]'
        }
        metadataSources { artifact() }
        content { includeGroup("jmeter-gradle-plugin") }
    }
}

3
很遗憾,带查询字符串的URL不能通过这种方式访问,'?'符号是由https://github.com/gradle/gradle/blob/master/subprojects/resources/src/main/java/org/gradle/internal/resource/ExternalResourceName.java#L115进行编码的。 - Akom
2
当我尝试这样做时,它不是在寻找.zip文件,而是在寻找.xml文件。有什么解决办法吗?我完全按照相同的指示操作了。 - Sourav Bhattacharjee
3
对我来说不起作用。 Gradle除了构件之外还在寻找一个ivy.xml文件。 - Victor Lyuboslavsky
1
这是一个很好的答案。我只想补充一点,依赖关系应该是compileOnly,以防止原始zip文件出现在运行时构件中。 - Andy Brown
1
我可以确认这适用于7.2,@rios0rios0你解决了吗? - elect
显示剩余11条评论

14
    plugins {
        id 'de.undercouch.download' version '4.0.0'
    }

    /**
     * The following two tasks download a ZIP file and extract its
     * contents to the build directory
     */
    task downloadZipFile(type: Download) {
        src 'https://github.com/gradle-download-task/archive/1.0.zip'
        dest new File(buildDir, '1.0.zip')
    }

    task downloadAndUnzipFile(dependsOn: downloadZipFile, type: Copy) {
        from zipTree(downloadZipFile.dest)
        into buildDir
    }

https://github.com/michel-kraemer/gradle-download-task


1
与Ant相比,这并不是一个好的解决方案,因为它非常慢。下载需要26分钟,而使用Chrome只需要2分钟。 - Michael S.

9

目前没有Gradle API可以从URL下载。您可以使用Ant、Groovy或者如果您想要受益于Gradle的依赖解析/缓存特性,可以将其视为具有自定义构件URL的Ivy存储库来实现此功能。解压缩可以按照通常的Gradle方式进行(copy方法或Copy任务)。


4
我希望能够得到一个例子...我是一个完全的Gradle新手! - xpmatteo
4
“如果您想从Gradle的依赖项解析/缓存功能中获得好处,可以将其视为一个带有自定义构件URL的Ivy存储库来使用”,这个答案可以举个例子吗? - CMPS
@CMPS 为什么你不用 Groovy? - RaGe
@RaGe 我目前的解决方案是使用 Groovy,它运行良好,但我想使用存储库来受益于缓存系统。 - CMPS
@CMPS 啊,缓存!我在下面发布了一个答案。 - RaGe

7

我尝试通过将from zipTree() & into outputDir移动到doLast { }中来将其移动到执行阶段。但我不明白的是,它从未起作用。即使在doLast中的println代码也无法工作。然而,这段代码在gradle配置阶段可以正常工作。为什么? - nashter
因为它配置了任务,执行仍然在执行阶段完成。实际上,在执行阶段更改配置是不应该的,而在您的情况下,执行阶段根本不会被执行,因为任务认为没有任何内容可复制,所以任务执行被跳过。但实际上,这个答案与问题无关,因为它不能用于HTTP URL。 - Vampire

5
这适用于Gradle 5(已测试过5.5.1):
task download {
    doLast {
        def f = new File('file_path')
        new URL('url').withInputStream{ i -> f.withOutputStream{ it << i }}
    }
}

调用 gradle download 可以将文件从 url 下载到 file_path

如果需要,您可以使用其他答案中的方法解压缩该文件。


同样适用于Gradle 4.7。如果有人需要的话。 - camikiller

1
我成功地运行了@RaGe的答案,但是我不得不做一些调整,因为ivy布局方法已经过时。请参阅https://docs.gradle.org/current/dsl/org.gradle.api.artifacts.repositories.IvyArtifactRepository.html#org.gradle.api.artifacts.repositories.IvyArtifactRepository:layout(java.lang.String,%20groovy.lang.Closure)
所以为了让它正常工作,我必须将其调整为适用于Tomcat Keycloak适配器的以下形式:
ivy {
    url 'https://downloads.jboss.org/'
    patternLayout {
        artifact '/[organization]/[revision]/adapters/keycloak-oidc/[module]-[revision].[ext]'
    }
}

dependencies {
    // https://downloads.jboss.org/keycloak/4.8.3.Final/adapters/keycloak-oidc/keycloak-tomcat8-adapter-dist-4.8.3.Final.zip
    compile "keycloak:keycloak-tomcat8-adapter-dist:$project.ext.keycloakAdapterVersion@zip"
}

task unzipKeycloak(type: Copy) {

    def zipPath = project.configurations.compile.find {it.name.startsWith("keycloak") }
    println zipPath
    def zipFile = file(zipPath)
    def outputDir = file("${buildDir}/tomcat/lib")

    from zipTree(zipFile)
    into outputDir
}

不幸的是,这个解决方案也让Gradle寻找一个Ivy XML文件。 - barfuin
无法工作。Gradle 7.2正在寻找普通的Maven依赖项。 - rios0rios0

1
"原生Gradle",仅包括下载部分(有关解压缩的其他答案请参见)。
task foo {
  def src = 'http://example.com/file.zip'
  def destdir = 'somedir'
  def destfile = "$destdir/file.zip"
  doLast {
    def url = new URL(src)
    def f = new File(destfile)
    if (f.exists()) {
      println "file $destfile already exists, skipping download"
    } else {
      mkdir "$destdir"
      println "Downloading $destfile from $url..."
      url.withInputStream { i -> f.withOutputStream { it << i } }
    }
  }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接