在完成 zip 创建之前将 zip 内容发送到响应

Posted

技术标签:

【中文标题】在完成 zip 创建之前将 zip 内容发送到响应【英文标题】:Send the zip content to response before you complete the zip creation 【发布时间】:2013-08-28 20:38:36 【问题描述】:

我已经成功地在我的“本地构建”中创建了一个临时文件夹并在其中添加图像文件以供用户压缩和下载。不幸的是,在部署到我的测试服务器后,我无法创建这样的临时文件夹,因此我相信由于权限错误而无法压缩和流式传输它。基本上我是通过了。我无法访问在我的测试服务器上创建文件夹,并且需要将此文件夹和文件存储在我的 S3 存储桶上,然后从这里创建一个 zipOutputStream - 或者 - 如果可能的话,我认为这可能是一个更好的解决方案,是在我完成 zip 创建之前“即时”发送 zip 内容以响应。这可能吗?如果是这样,将如何去做呢?与将文件临时存储在 S3 上以进行压缩和流式传输相比,此方法是否有好处。

用于文件夹创建和压缩和流式传输的当前代码

def downloadZip()

    def fName = params.fName // ZipFile Name passed in 'example.zip'
    def fLoc = params.fLoc   //Folder Location passed in '/example'
    def user = User.get( fLoc as Long ) //Get the Users files to be zipped              
    def urlList = [] 
    List ownedIds

    //Create a temporary directory to place files inside before zipping
    new File(fLoc).mkdir()

    //Dynamic Resource 'http://example.com/' -or- 'http://localhost:8080/'
    def location = "$resource( dir:'/', absolute:true )" 
    //Collect and Download QR-Codes image files
    ownedIds = user.geolinks.collect 
        //Define Url for Download 
        def urls = (location+"qrCode/download?u=http%3A%2F%2Fqr.ageoa.com%2F" +it.linkAddress+ "&s=150&n=" +it.linkAddress)         
        //Download each QR-Code
        download2(urls,fLoc)
    

    //ZIP the directory that was created and filled with the QR codes
    String zipFileName = fName
    String inputDir = fLoc

    ZipOutputStream zipFile = new ZipOutputStream(new FileOutputStream(zipFileName))
    new File(inputDir).eachFile()  file ->
        zipFile.putNextEntry(new ZipEntry(file.getName()))
        def buffer = new byte[1024]
        file.withInputStream  i ->
            def l = i.read(buffer)
            // check whether the file is empty
            if (l > 0) 
                zipFile.write(buffer, 0, l)
            
        
        zipFile.closeEntry()
    
    zipFile.close()

    //Download QR-Code Zip-File
    try 
        def file = new File(fName)    
        response.setContentType("application/octet-stream")
        response.setHeader("Content-disposition", "attachment;filename=$file.getName()")
        response.outputStream << file.newInputStream() // Performing a binary stream copy                           
           
    catch(Exception e)
            e.printStackTrace()
       

    //Delete Temporary Folder
    def dir2 = new File(fLoc)
    dir2.deleteDir()
 

//Download All QR-Codes images to folder [userID]
def download2(address, dir)
    def file = new FileOutputStream(dir+"/"+address.tokenize("&n=")[-1]+".png")
    if(file)
        def out = new BufferedOutputStream(file)
        out << new URL(address).openStream()
        out.close()
    

【问题讨论】:

你不能只做ZipOutputStream zipFile = new ZipOutputStream( response.outputStream )然后做你正在做的事情吗? 感谢蒂姆的提示,但是如果我不能将图像文件首先放在我正在创建/下载的服务器上,我该如何加载 zipOutputStream 和我的内容?如何在流中用我的内容填充这个 zipFile 我猜这也是我要问的?我是否在 zipFile.putNextEntry() 中以某种方式构建它你是否有一个例子是如何完成的,或者我有足够的代码让你切碎和操作?顺便说一句,无论如何感谢您的提示。 添加了一个答案,这是我的意思的一个工作示例......希望它有所帮助! 【参考方案1】:

好吧,这应该可以,如果其中任何一个没有意义,请告诉我......

// A list of things to download and add to the zip
List<URL> testList = [ 'http://cdn.sstatic.net/***/img/sprites.png?v=6',
                       'https://www.google.co.uk/images/srpr/logo4w.png' ]*.toURL()
response.setHeader( "Content-disposition", "attachment; filename=resources.zip" )
response.contentType = "application/octet-stream"

// Wrap the response stream in a zipoutputstream
new ZipOutputStream( response.outputStream ).withStream  zos ->

    // Always add a root folder to zip files, not to do so is spiteful
    zos.putNextEntry( new ZipEntry( "resources/" ) )

    // For each URL
    testList.each  res ->

        // Get a name for this file in the zip 

        // This bit might be the tricky bit as I guess you don't know the file
        // names. So instead of this you might need to check the response
        // object from opening a connection to the URL.  However, without a
        // working example URL from you, I can't be sure :-(
        String name = res.path.split( '/' )[ -1 ]

        // Create a zip entry for it
        zos.putNextEntry( new ZipEntry( "resources/$name" ) )

        // Write the resource stream into our zip
        res.withInputStream  ins ->
            zos << ins
        

        // Close this resource
        zos.closeEntry() 
    
    // Close the root folder
    zos.closeEntry()

【讨论】:

谢谢你的例子。我能够从我想要的图像中创建一个 url 数组,用 .tokenize() 命名它们,并将它们填充到 zip 流中。 :)

以上是关于在完成 zip 创建之前将 zip 内容发送到响应的主要内容,如果未能解决你的问题,请参考以下文章

下载Ajax响应作为zip文件?

可以将 7-zip 的标准输出提供给 PHP 脚本吗?

将 ZIP 文件发送到基于 REST 的 API,该 API 使用基于 Flutter 的移动应用程序托管在 AWS 上的 SSL TLS (https)

使用 SharpZipLib 在 .net 中通过 http 流式传输 zip 文件

Spring REST - 创建 .zip 文件并将其发送到客户端

加密的zip文件,在PHP 7.2之前