简体   繁体   English

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

[英]Servlet - how to download multiples files from database

I look around and it seems that zipping all the files together is the way to go. 我环顾四周,似乎将所有文件压缩在一起就是要走的路。 If that the case this is the design I am thinking of doing. 如果是这种情况,这就是我想要做的设计。 Please let me know if there is more efficient way of doing this 如果有更有效的方法,请告诉我

  • Client select multiples file of downloading, then click Download 客户端选择下载的多个文件,然后单击“下载”
  • servlet receive the requests, then do multiple SELECT (files are saved as blob objects) statement to the database. servlet接收请求,然后将多个SELECT(文件保存为blob对象)语句写入数据库。

I can create BufferedOutputStream and write the blobs to different files, and I guess after I done doing that, I can zip the files up. 我可以创建BufferedOutputStream并将blob写入不同的文件,我想在完成之后,我可以压缩文件。 (is this a good way to zip all the files or is there a better and faster way of achieve this?) After done zipping, then send it to the client (not sure how to do that either, please anyone know how to, please help) Please point out if there is any flaw in my design. (这是拉链所有文件的好方法还是有更好更快的方法来实现这个?)完成拉链后,然后发送给客户端(不知道怎么做,请任何人都知道如何,请帮助)请指出我的设计是否有任何缺陷。 I post some questions above, and would really appreciated of anyone can help me answer though. 我在上面发布了一些问题,并且真的很感激,任何人都可以帮我回答。 Sample code would be terrific. 示例代码非常棒。 Thank you very much and have a wonderful new year 非常感谢你,祝你新年快乐

Basically you just need to construct a new ZipOutputStream around response.getOutputStream() and then add every InputStream from the DB as new ZipEntry . 基本上你只需要在response.getOutputStream()周围构建一个新的ZipOutputStream ,然后将每个InputStream从DB添加为新的ZipEntry To improve performance, you can wrap the response.getOutputStream() in a BufferedOutputStream beforehand and the InputStream in a BufferedInputStream and use a byte[] buffer. 为了提高性能,可以包住response.getOutputStream()BufferedOutputStream预先和所述InputStreamBufferedInputStream并使用byte[]缓冲液中。

Here's a basic example: 这是一个基本的例子:

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) { /**/ }
        }
    }

}

which can be invoked by a form like this: 可以通过以下形式调用:

<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>

That said, do in no way use ByteArrayInputStream/ByteArrayOutputStream as some here may suggest. 这就是说,做到任何方式使用ByteArrayInputStream/ByteArrayOutputStream一些这里可能建议。 Those are namely backed by a raw byte[] . 那些是由原始byte[] You may risk that that your application breaks when the size of all of the files (from all concurrent users!) together inside bytearrays is larger than the available server memory. 当bytearrays中的所有文件(来自所有并发用户!)的大小大于可用服务器内存时,您可能会冒应用程序中断的风险。 You already have a stream from the DB and a stream to the response. 您已有来自数据库的流和响应的流。 Just pipe them through a small byte buffer in a read/write loop. 只需将它们通过读/写循环中的小字节缓冲区进行管道传输即可。 You don't need to get hold of the entire inputstream in a (under the hood) massive byte buffer and then write it away to the outputstream. 您不需要在(引擎盖下)大量字节缓冲区中获取整个输入流,然后将其写入输出流。

You shouldn't need to write the files first. 您不应该首先编写文件。 You can create a zip file. 您可以创建一个zip文件。

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

Then you can add entries for each of the files you are reading from the database 然后,您可以为从数据库中读取的每个文件添加条目

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

Of course the writes could be done in a loop so the byteArrayOfFileEntryData won't use up all the server memory. 当然,写入可以在循环中完成,因此byteArrayOfFileEntryData不会耗尽所有服务器内存。

Once all the zip entries are added, close the zip file 添加完所有zip条目后,请关闭zip文件

zipOutStream.close();

Once the zip file is created you'll still need to return that to the user. 创建zip文件后,您仍然需要将其返回给用户。

Edit: One option is to create the zip file output stream wrapping the response output stream. 编辑:一个选项是创建包装响应输出流的zip文件输出流。 That way the zip file doesn't need to be stored on the server either. 这样,zip文件也不需要存储在服务器上。 I haven't tested this option though. 我没有测试过这个选项。

zipOutStream = new ZipOutputStream(response.getOutputStream())

We do the zip file building on a separate machine which has its own Tomcat server to return the zip file. 我们在单独的机器上构建zip文件,该机器具有自己的Tomcat服务器以返回zip文件。 The user is presented with a "wait" page showing a list of the files they selected which automatically refreshes to show the link to the zip file once all the files are zipped. 向用户显示一个“等待”页面,显示他们选择的文件列表,这些文件会在压缩所有文件后自动刷新以显示zip文件的链接。 This way the user can rethink the download once they see the size of the zip file. 这样,一旦用户看到zip文件的大小,就可以重新考虑下载。 It also keeps the zip files off the main application server. 它还将zip文件保留在主应用程序服务器之外。

You do not need to write the database blobs to temporary files as you can create the zip file on the fly in your servlet. 您不需要将数据库blob写入临时文件,因为您可以在servlet中动态创建zip文件。 This includes both the complete zip file and all the entries. 这包括完整的zip文件和所有条目。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM