开发者

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.

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. (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 app开发者_Go百科reciated 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. To improve performance, you can wrap the response.getOutputStream() in a BufferedOutputStream beforehand and the InputStream in a BufferedInputStream and use a byte[] buffer.

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. Those are namely backed by a raw 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. 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.

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.

Once all the zip entries are added, close the zip file

zipOutStream.close();

Once the zip file is created you'll still need to return that to the user.

Edit: One option is to create the zip file output stream wrapping the response output stream. That way the zip file doesn't need to be stored on the server either. 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. 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. This way the user can rethink the download once they see the size of the zip file. It also keeps the zip files off the main application server.


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. This includes both the complete zip file and all the entries.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜