Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/database/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java Servlet-如何从数据库下载多个文件_Java_Database_Servlets_Download - Fatal编程技术网

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

Java Servlet-如何从数据库下载多个文件,java,database,servlets,download,Java,Database,Servlets,Download,我环顾四周,似乎将所有文件压缩在一起是一种方法。如果是这样的话,这就是我想做的设计。如果有更有效的方法,请告诉我 客户端选择多个下载文件,然后单击下载 servlet接收请求,然后对数据库执行多个SELECT(文件保存为blob对象)语句 我可以创建BufferedOutputStream并将blob写入不同的文件,我想在我完成之后,我可以将文件压缩。(这是压缩所有文件的好方法还是有更好更快的方法?) 完成压缩后,将其发送给客户(也不确定如何进行压缩,请任何人知道如何进行压缩,请帮助) 请指

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

  • 客户端选择多个下载文件,然后单击下载
  • servlet接收请求,然后对数据库执行多个SELECT(文件保存为blob对象)语句
我可以创建BufferedOutputStream并将blob写入不同的文件,我想在我完成之后,我可以将文件压缩。(这是压缩所有文件的好方法还是有更好更快的方法?) 完成压缩后,将其发送给客户(也不确定如何进行压缩,请任何人知道如何进行压缩,请帮助)
请指出我的设计是否有任何缺陷。我在上面贴了一些问题,如果有人能帮我回答,我将不胜感激。示例代码将非常棒。非常感谢您,祝您新年愉快

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

不需要先写文件。您可以创建一个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文件从主应用程序服务器上移除。

基本上,您只需围绕
响应.getOutputStream()
构建一个新的
zipoutStream
,然后将数据库中的每个
InputStream
添加为新的
ZipEntry
。为了提高性能,您可以事先将
响应.getOutputStream()
包装在
缓冲输出流中,将
输入流
包装在
缓冲输出流中
并使用
字节[]
缓冲区

以下是一个基本示例:

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>

foo.exe
bar.pdf
waa.doc
baz.html

也就是说,不要像这里有些人建议的那样使用
ByteArrayInputStream/ByteArrayOutputStream
。它们由原始的
字节[]
支持。当ByteArray中所有文件(来自所有并发用户!)的大小加在一起大于可用的服务器内存时,您可能会面临应用程序崩溃的风险。您已经有一个来自DB的流和一个到响应的流。只需在读/写循环中通过一个小字节缓冲区就可以了。您不需要将整个输入流保存在(引擎盖下的)大字节缓冲区中,然后将其写入输出流。

您编辑了答案,但byteArrayOfFileEntryData的
听起来仍然不正确。而是使用
inputStreamOfFileEntryData
。我不知道数据是如何来自数据库的,但要写入的参数是int或byte[](或byte[],带有起始位置和长度)。唯一(正确)的方法是
ResultSet 35; getBinaryStream()
返回一个
输入流
。很抱歉,我花了这么长时间才回复。我有些事要处理。当我阅读你的代码时,你有你的FileDao和你的DaoFactory,我似乎没有这些类。它们是您创建的类吗?它们只是具有自我解释名称的虚拟DAO(数据访问对象)类/方法。如果您的数据访问层代码设计得很好,那么您应该已经拥有了它们。如果没有,我建议你到这里来:谢谢。我读了你的博客。优秀的作品。我只是想检查一下我的理解是否准确。yourFileItem是一个包含文件信息的类,例如名称、文件ID。。。2.item.getInputStream()--我对此有点困惑。你能帮我解决这个问题吗?我猜我们正在创建一个InputStream来检索数据库上的blob文件。请原谅我的无知!!!您可以使用
ResultSet#getBinaryStream()
来解决这个问题:为什么您的FileItem有一个方法调用getInputStream()。我认为getInputStream()方法返回结果集#getBinaryStream(),它位于FileDao类中。