在客户端下载时创建 Zip 文件

Ali*_*han 3 java io zip multithreading

我尝试开发类似 dropbox(非常基本的)的东西。下载一个文件,这真的很容易。只需使用servletoutputstream。我想要的是:当客户端询问我多个文件时,我在服务器端压缩文件然后发送给用户。但是如果文件很大,压缩它们并发送给用户需要太多次。

有没有办法在压缩文件时发送文件?

谢谢你的帮助。

GPI*_*GPI 5

用于 ZIP 文件的部分 Java API 实际上旨在提供“动态”压缩。这一切都非常适合 java.io API 和 servlet API,这意味着这甚至......有点简单(不需要多线程 - 即使出于性能原因,因为通常您的 CPU 在 ZIPping 时可能比您的网络更快将在发送内容)。

您将与之交互的部分是ZipOutputStream. 它是一个FilterOutputStream(这意味着它旨在包装一个outputstream已经存在的 - 在您的情况下,这将是响应的OutputStream),并将使用 ZIP 压缩压缩您发送的每个字节。

所以,假设你有一个 get 请求

protected void doGet(HttpServletRequest req, HttpServletResponse resp)
    throws ServletException, IOException {

    // Your code to handle the request
    List<YourFileObject> responseFiles = ... // Whatever you need to do 

    // We declare that the response will contain raw bytes
    response.setContentType("application/octet-stream");

    // We open a ZIP output stream
    try (ZipOutputStream zipStream = new ZipOutputStream(response.getOutputStream()) {// This is Java 7, but not that different from java 6

        // We need to loop over each files you want to send
        for(YourFileObject fileToSend : responseFiles) {
            // We give a name to the file
            zipStream.putNextEntry(new ZipEntry(fileToSend.getName()));
            // and we copy its content
            copy(fileToSend, zipStream);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

当然,您应该进行适当的异常处理。不过有几个快速说明:

  1. ZIP 文件格式要求每个文件都有一个名称,因此每次启动新文件时都必须创建一个新的 ZipEntry(IllegalStateException无论如何,如果不这样做,您可能会得到一个)
  2. 正确使用 API 是在完成写入后关闭每个条目(在文件末尾)。但是:Java 实现会为您执行此操作:每次调用putNextEntry它时都会自行关闭前一个(如果需要)
  3. 同样,您不能忘记关闭 ZIP 流,因为这将正确关闭最后一个条目并刷新创建正确 ZIP 文件所需的所有内容。否则将导致文件损坏。在这里, try with resources 语句执行此操作:ZipOutputStream一旦所有内容都写入它,它就会关闭。
  4. copy这里的方法正是您用来将所有字节从原始文件传输到输出流的方法,它没有任何特定于 ZIP 的内容。就打电话outputStream.write(byte[] bytes)

**编辑:** 澄清...

例如,给定YourFileType具有以下方法的 a :

public interface YourFileType {

    public byte[] getContent();

    public InputStream getContentAsStream();

}
Run Code Online (Sandbox Code Playgroud)

然后复制方法可能看起来像(这都是非常基本的Java IO,您可以使用诸如commons io之类的库来不重新发明轮子......)

public void copy(YourFileType file, OutputStream os) throws IOException {
    os.write(file.getContent());
}
Run Code Online (Sandbox Code Playgroud)

或者,对于完整的流媒体实现:

public void copy(YourFileType file, OutputStream os) throws IOException {
    try (InputStream fileContent = file.getContentAsStream()) {
        byte[] buffer = new byte[4096]; // 4096 is kind of a magic number
        int readBytesCount = 0;
        while((readBytesCount = fileContent.read(buffer)) >= 0) {
            os.write(buffer, 0, readBytesCount);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

使用这种实现,您的客户端几乎在您开始写入时就开始接收响应ZIPOutputStream(唯一的延迟是内部缓冲区的延迟),这意味着它不应超时(除非您花费太长时间构建要发送的内容) - 但这不是 ZIPping 部分的错误)。