我可以在 vaadin 中直接流式传输 ZipFile 吗?

Can I do direct streaming of ZipFile in vaadin?

我的应用程序的当前架构不允许我在服务器端存储文件并为该存储的文件创建 link。那么是否有任何其他选项(或代码片段)可以直接流式传输 ZipFile 并将其存储在客户端?

编辑: 我想我的问题被误解了。我收到压缩文件并将其存储在客户端的答案。我已经做到了。以下是示例用例的主要关注点:

场景: 用户有大约 5000 条记录(每条大约 1 MB)并且用户想要下载以 ZIP 格式压缩的每 5000 条记录的子记录(CSV 格式)。所有 CSV 文件都是即时生成的。

方法: 由于 ZIP 文件的大小可达 5 GB,因此我采用了将文件内容直接流式传输到客户端创建的 ZIP 文件的方法。为此,我使用了 PipeInputStream 和 PipeOutputStream。

结果: 由于我是 vaadin 的新手,所以我在上述方法中没有成功,所以寻找支持直接将 ZIP 文件(无论大小)流式传输到客户端的任何建议/代码片段。

我想我现在清楚了。

您不必创建 ZIP 包来通过 HTTP 压缩单个文件。所有体面的浏览器都支持读取压缩的 HTTP 响应[1],所以让我们利用它:

@Theme("mytheme")
@Widgetset("org.test.MyAppWidgetset")
public class MyUI extends UI {

    @Override
    protected void init(VaadinRequest vaadinRequest) {
        Button downloadButton = new Button("Download File");

        StreamResource myResource = createResource();
        FileDownloader fileDownloader = new FileDownloader(myResource);
        fileDownloader.extend(downloadButton);

        setContent(downloadButton);
    }

    private StreamResource createResource() {
        return new StreamResource(new StreamResource.StreamSource() {
            @Override
            public InputStream getStream() {
                InputStream s = null;
                try {
                    s = new DeflaterInputStream(new FileInputStream("/tmp/some.csv"));
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                }
                return s;

            }
        }, "some.csv") {
            @Override
            public DownloadStream getStream() {
                DownloadStream ds =  super.getStream();
                ds.setParameter("Content-Encoding", "deflate");
                return ds;
            }
        };
    }

    @WebServlet(urlPatterns = "/*", name = "MyUIServlet", asyncSupported = true)
    @VaadinServletConfiguration(ui = MyUI.class, productionMode = false)
    public static class MyUIServlet extends VaadinServlet {
    }
}

浏览器将下载压缩流,但保存原始文件,即时解压缩。

优点:这非常节省内存。

缺点:您不能将多个文件组合成一个 ZIP 存档。

[1] https://en.wikipedia.org/wiki/HTTP_compression

你的问题不清楚。始终在发布问题时进行一些研究。现在,问题来自您想流式传输一些 URL、文件共享、数据库的地方。您使用的 Vaadin 版本是 6< 还是 >6?

已编辑问题分为两部分。

  1. 直接从网站下载 Zip 文件。以下是用于下载任何文件的示例代码片段。

    import java.io.BufferedInputStream;
    import java.io.ByteArrayInputStream;
    import java.io.ByteArrayOutputStream;
    import java.io.InputStream;
    import java.net.URL;
    
    import javax.servlet.annotation.WebServlet;
    
    import com.vaadin.annotations.Theme;
    import com.vaadin.annotations.VaadinServletConfiguration;
    import com.vaadin.server.FileDownloader;
    import com.vaadin.server.StreamResource;
    import com.vaadin.server.StreamResource.StreamSource;
    import com.vaadin.server.VaadinRequest;
    import com.vaadin.server.VaadinServlet;
    import com.vaadin.ui.Button;
    import com.vaadin.ui.UI;
    
    @SuppressWarnings("serial")
    @Theme("vaadintest")
    public class VaadintestUI extends UI {
    
        @WebServlet(value = "/*", asyncSupported = true)
        @VaadinServletConfiguration(productionMode = false, ui = VaadintestUI.class)
        public static class Servlet extends VaadinServlet {
        }
    
        protected void init(VaadinRequest request) {
            Button downloadButton = new Button("Download Zip File");
    
            StreamResource myResource = createResource();
            FileDownloader fileDownloader = new FileDownloader(myResource);
            fileDownloader.extend(downloadButton);
    
            setContent(downloadButton);
        }
    
    private StreamResource createResource() {
        return new StreamResource(new StreamSource() {
            @Override
            public InputStream getStream() {
    
                BufferedInputStream in = null;
                ByteArrayOutputStream bao = null;
                try {
                    in = new BufferedInputStream(
                            new URL("http://www-us.apache.org/dist/commons/io/binaries/commons-io-2.5-bin.zip" + "")
                                    .openStream());
                    byte[] buff = new byte[8000];
                    int bytesRead = 0;
                    bao = new ByteArrayOutputStream();
    
                    while ((bytesRead = in.read(buff)) != -1) {
                        bao.write(buff, 0, bytesRead);
                    }
    
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return new ByteArrayInputStream(bao.toByteArray());
    
            }
        }, "somefile.zip");
    }
    

    }

  2. 压缩巨大的 CSV 文件并下载

参考:Best Practices to Create and Download a huge ZIP (from several BLOBs) in a WebApp

在服务器端压缩和存储很容易。我不认为由于访问问题直接存储到客户端磁盘会更容易(使用 OutputStream 是可能的)。您可以将其压缩在服务器端并共享 link 到压缩文件的客户端(文件需要存储在 WebApp 文件夹中)。

此外,您可以在压缩下载后使用输出流 it.Below 是一个简单的例子。

参考:http://www.mkyong.com/java/how-to-download-file-from-website-java-jsp/

编辑问题,给出实际场景:然后分享代码片段就容易了

您可以参考的一些有用链接如下:

  1. Best Practices to Create and Download a huge ZIP (from several BLOBs) in a WebApp
  2. How to split a huge zip file into multiple volumes?
  3. Zip files with Java: Is there a limit?
  4. Very large zip file (> 50GB) --> ZipException: invalid CEN header
  5. Java multithreaded file downloading performance
  6. In Java: How to zip file from byte[] array?
  7. Uncompressing a ZIP file in memory in Java
  8. Best way to detect if a stream is zipped in Java

看起来 apache.commons.compress ZipArchiveOutputStream 是您问题的答案。但是你应该知道你的情况有一组限制(没有 RandomAccessFile)。简单示例(使用标准输出重定向:java -cp 测试 > test.zip):

import org.apache.commons.compress.archivers.ArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
import java.util.Arrays;

public class Test {

public static void main(String[] args) throws Exception {
      ZipArchiveOutputStream zipOut = new ZipArchiveOutputStream(System.out);
      byte[] data = new byte[1024*1024];
      for(byte i=49; i<120; i++) {
          Arrays.fill(data, i);
          ArchiveEntry file1 = new ZipArchiveEntry("file" + i + ".txt");
          zipOut.putArchiveEntry(file1);
          zipOut.write(data);
          zipOut.closeArchiveEntry();
       }
       zipOut.finish();
       zipOut.close();
    }
}

我看到这个答案已经被系统自动接受了(不是我)因为它被投票了 3 次。我对此没有意见,但我不想让那些(以防万一)来到这里寻找答案的人感到困惑。我很欣赏@Pratyush Kumar Singh 给出的答案(因为它可以帮助您探索更多)但是抱歉 Whosebug 我不能接受这个答案我的问题还没有得到解答。我仍在努力,一旦我通过 POC 解决,我将 post 给出答案。

Java JDK 和 Apache commons-compress 不允许您懒惰地流式传输 ZIP 存档,因此我实现了一个 Java ZIP 库 [1] 来处理这个问题。目前的限制是它不支持 ZIP64 扩展,这意味着它不能压缩大于 4 GiB 的文件并且不能生成大于 4 GiB 的档案。我正在努力。

[1] https://github.com/tsabirgaliev/zip