Servlet - 如何从数据库下载多个文件

时间:2009-12-29 17:21:26

标签: java database servlets download

我环顾四周,似乎将所有文件压缩在一起就是要走的路。如果是这种情况,这就是我想要做的设计。如果有更有效的方法,请告诉我

  • 客户端选择下载的多个文件,然后单击下载
  • servlet接收请求,然后将多个SELECT(文件保存为blob对象)语句写入数据库。

我可以创建BufferedOutputStream并将blob写入不同的文件,我想在完成后我可以将文件压缩起来。 (这是压缩所有文件的好方法还是有更好更快的方法来实现这个?) 完成压缩后,然后将其发送给客户端(不知道如何做到这一点,请任何人都知道如何,请帮助) 请指出我的设计是否有任何缺陷。我在上面发布了一些问题,并且真的很感激,任何人都可以帮我回答。示例代码非常棒。非常感谢你,祝你有一个美好的新年

3 个答案:

答案 0 :(得分:11)

基本上,您只需要在ZipOutputStream周围构建一个新的response.getOutputStream(),然后将数据库中的每个InputStream添加为新的ZipEntry。为了提高性能,您可以事先将response.getOutputStream()包裹在BufferedOutputStream中,将InputStream包裹在BufferedInputStream中并使用byte[]缓冲区。

这是一个基本的例子:

package com.example;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class ZipFileServlet extends HttpServlet {

    private static final int DEFAULT_BUFFER_SIZE = 10240; // 10KB.
    private YourFileDAO yourFileDAO = YourDAOFactory.getYourFileDAO();

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException
    {
        String[] fileIds = request.getParameterValues("fileId");
        response.setContentType("application/zip");
        response.setHeader("Content-Disposition", "attachment; filename=\"allfiles.zip\"");
        ZipOutputStream output = null;
        byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];

        try {
            output = new ZipOutputStream(new BufferedOutputStream(response.getOutputStream(), DEFAULT_BUFFER_SIZE));

            for (String fileId : fileIds) {
                YourFileItem item = yourFileDAO.find(fileId);
                if (item == null) continue; // Handle yourself. The fileId may be wrong/spoofed.
                InputStream input = null;

                try {
                    input = new BufferedInputStream(item.getInputStream(), DEFAULT_BUFFER_SIZE);
                    output.putNextEntry(new ZipEntry(item.getName()));
                    for (int length = 0; (length = input.read(buffer)) > 0;) {
                        output.write(buffer, 0, length);
                    }
                    output.closeEntry();
                } finally {
                    if (input != null) try { input.close(); } catch (IOException logOrIgnore) { /**/ }
                }
            }
        } finally {
            if (output != null) try { output.close(); } catch (IOException logOrIgnore) { /**/ }
        }
    }

}

可以通过以下形式调用:

<form action="zipFile" method="post"> 
    <input type="checkbox" name="fileId" value="1"> foo.exe<br>
    <input type="checkbox" name="fileId" value="2"> bar.pdf<br>
    <input type="checkbox" name="fileId" value="3"> waa.doc<br>
    <input type="checkbox" name="fileId" value="4"> baz.html<br>
    <input type="submit" value="download zip">
</form>

那就是说,在没有办法中使用ByteArrayInputStream/ByteArrayOutputStream,正如这里可能建议的那样。那些是由原始byte[]支持的。当bytearrays中的所有文件(来自所有并发用户!)的大小大于可用服务器内存时,您可能会冒应用程序中断的风险。您已有来自数据库的流和响应的流。只需将它们通过读/写循环中的小字节缓冲区进行管道传输即可。您不需要在(引擎盖下)大量字节缓冲区中获取整个输入流,然后将其写入输出流。

答案 1 :(得分:5)

您不需要先写文件。您可以创建一个zip文件。

FileOutputStream fos = new FileOutputStream(zipFileName);
zipOutStream = new ZipOutputStream(fos);

然后,您可以为正在从数据库中读取的每个文件添加条目

ZipEntry zipEntry = new ZipEntry("NameOfFileToBeAdded");
zipOutStream.putNextEntry(zipEntry);
zipOutStream.write(byteArrayOfFileEntryData);
zipOutStream.closeEntry();

当然写入可以在循环中完成,因此byteArrayOfFileEntryData不会耗尽所有服务器内存。

添加完所有zip条目后,请关闭zip文件

zipOutStream.close();

创建zip文件后,您仍然需要将其返回给用户。

编辑:一个选项是创建包装响应输出流的zip文件输出流。这样,zip文件也不需要存储在服务器上。我没有测试过这个选项。

zipOutStream = new ZipOutputStream(response.getOutputStream())

我们在单独的机器上构建zip文件,该机器具有自己的Tomcat服务器以返回zip文件。向用户显示一个“等待”页面,显示他们选择的文件列表,这些文件会在压缩所有文件后自动刷新以显示zip文件的链接。这样,一旦用户看到zip文件的大小,就可以重新考虑下载。它还将zip文件保留在主应用程序服务器之外。

答案 2 :(得分:1)

您不需要将数据库blob写入临时文件,因为您可以在servlet中动态创建zip文件。这包括完整的zip文件和所有条目。