简体   繁体   English

压缩大量pdf文件并下载:OutOfMemoryError:Java堆空间

[英]Zip a great number of pdf file and download it: OutOfMemoryError: Java heap space

I want create a zip file to download it. 我想创建一个zip文件来下载它。 I have a great number of pdf file.. 我有大量的pdf文件。

I need to zip them into sub-zip of 50MB one, so I create a master zip that contain all other sub-zip.. 我需要将它们压缩为50MB的子zip,所以我创建了一个包含所有其他子zip的主zip。

But when run my code I obtain: 但是当运行我的代码时,我得到:

Grave: Servlet.service() for servlet [appServlet] in context with path [/myProg] threw exception [Handler processing failed; nested exception is java.lang.OutOfMemoryError: Java heap space] with root cause
java.lang.OutOfMemoryError: Java heap space

This is my code to divide and zip all files: 这是我分割和压缩所有文件的代码:

public void zippatore(List<File> filesDaZippare, ZipOutputStream masterZos, String zipName, String pdfName) throws IOException{
    float newSize = 0;
    boolean first = true;
    int count = 1;
    File file;
    ByteArrayOutputStream subBaos = new ByteArrayOutputStream();
    ZipOutputStream subZos = new ZipOutputStream(subBaos);

    masterZos.putNextEntry(new ZipEntry(zipName+" "+count++ +".zip"));
    while(filesDaZippare.size() > 0){
        file = filesDaZippare.remove(0);
        FileInputStream fis = new FileInputStream(file);
        newSize += file.length();
        String fileName = file.getName().substring(2);
        if(newSize < MAX_SIZE || first){
            subZos.putNextEntry(new ZipEntry(pdfName+" "+fileName+".pdf"));
            IOUtils.copy(fis, subZos);
            file.delete();
            subZos.closeEntry();
            first = false;
        }else{
            subZos.flush();
            subZos.close();

            masterZos.write(subBaos.toByteArray());
            masterZos.closeEntry();
            masterZos.putNextEntry(new ZipEntry(zipName+" "+count++ +".zip"));

            subZos = new ZipOutputStream(subBaos);
            subZos.putNextEntry(new ZipEntry(pdfName+" "+fileName+".pdf"));
            IOUtils.copy(fis, subZos);
            file.delete();

            newSize = file.length();
            first = true;
        }
        subZos.closeEntry();
    }
    subZos.closeEntry();
    subZos.flush();
    subZos.close();
    masterZos.write(subBaos.toByteArray());

    masterZos.closeEntry();
}

How can I fix it?? 我该如何解决?

EDIT: I added the code to save in a tmp folder my sub-zip file.. And than I do this: 编辑:我添加了代码以将我的子zip文件保存在tmp文件夹中。

    File folder = new File(VariabiliGlobali.PATH_TMP);
    byte[] buffer = new byte[1024];

    for(File file : folder.listFiles()) {
        FileInputStream fis = new FileInputStream(file);
        masterZos.putNextEntry(new ZipEntry(file.getName()));

        int length;
        while ((length = fis.read(buffer)) > 0) {
            masterZos.write(buffer, 0, length);
        }

        masterZos.closeEntry();
        fis.close();
    }
    masterZos.flush();
    masterZos.close();

to get all zip file and put in my master zip file, but I obtain the same error when I put them in it... 获取所有zip文件并将其放入我的主zip文件中,但是将它们放入主zip文件中时会遇到相同的错误...

I solved in this way: for master zip file I used a FileOutputStream , while to all others sub-zip files I use a ByteArrayOutputStream and I flushed and close it every 50 MB. 我是这样解决的:对于主zip文件,我使用了FileOutputStream ,而对所有其他子zip文件,我使用了ByteArrayOutputStream ,并且每隔50 MB刷新并关闭它一次。

So I don't use a file to every sub-zip, and so using a stream is a bit quickly than use a file.. 因此,我不会对每个子zip文件都使用文件,因此使用流比使用文件要快。

This is the code: 这是代码:

    FileOutputStream masterBos = new FileOutputStream(VariabiliGlobali.PATH_TMP+"Tmp.zip");
    ZipOutputStream masterZos = new ZipOutputStream(masterBos);


public void zippatore(List<File> filesDaZippare, ZipOutputStream masterZos, String zipName, String pdfName) throws IOException{
    float newSize = 0;
    boolean first = true;
    int count = 1;
    File file;
    ByteArrayOutputStream subBaos = new ByteArrayOutputStream();
    ZipOutputStream subZos = new ZipOutputStream(subBaos);

    masterZos.putNextEntry(new ZipEntry(zipName+" "+count++ +".zip"));
    while(filesDaZippare.size() > 0){
        file = filesDaZippare.remove(0);
        FileInputStream fis = new FileInputStream(file);
        newSize += file.length();
        String fileName = file.getName().substring(2);
        if(newSize < MAX_SIZE || first){
            subZos.putNextEntry(new ZipEntry(pdfName+" "+fileName+".pdf"));
            IOUtils.copy(fis, subZos);
            file.delete();
            subZos.closeEntry();
            first = false;
        }else{
            subZos.flush();
            subZos.close();
            subBaos.flush();
            subBaos.close();

            masterZos.write(subBaos.toByteArray());
            masterZos.closeEntry();
            masterZos.putNextEntry(new ZipEntry(zipName+" "+count++ +".zip"));

            subBaos = new ByteArrayOutputStream();
            subZos = new ZipOutputStream(subBaos);
            subZos.putNextEntry(new ZipEntry(pdfName+" "+fileName+".pdf"));
            IOUtils.copy(fis, subZos);
            file.delete();

            newSize = file.length();
            first = true;
        }
        subZos.closeEntry();
    }
    subZos.closeEntry();
    subZos.flush();
    subZos.close();
    masterZos.write(subBaos.toByteArray());

    masterZos.closeEntry();
}

From the code it can be seen that you are Zipping each of the sub zip into an outputstream backed by a ByteArrayOutput stream, which will consume memory. 从代码中可以看出,您正在将每个子zip压缩到一个由ByteArrayOutput流支持的输出流中,这将消耗内存。 Writing each of the Sub file into a temporary location on the disk and then pushing the individual zip into the master zip would reduce your memory need, at the same time writing into the disk has an overhead, but if the overall size is not known this would be a better approach. 将每个Sub文件写入磁盘上的临时位置,然后将单个zip推入主zip会减少您的内存需求,同时写入磁盘会产生开销,但是如果总体大小未知会是更好的方法。

Writing your temporary zip/subzip to a FileOutputstream instead of a ByteArrayOutputStream would allow you to continue as long as the disk has enough space to handle it. 只要磁盘有足够的空间来处理它,就可以将临时zip / subzip写入FileOutputstream而不是ByteArrayOutputStream来继续操作。

public void zippatore(List<File> filesDaZippare, ZipOutputStream masterZos, String   zipName, String pdfName) throws IOException{
    float newSize = 0;
    boolean first = true;
    int count = 1;
    File file;
    ByteArrayOutputStream subBaos = new ByteArrayOutputStream();
    ZipOutputStream subZos = new ZipOutputStream(subBaos);

    masterZos.putNextEntry(new ZipEntry(zipName+" "+count++ +".zip"));
    while(filesDaZippare.size() > 0){
    file = filesDaZippare.remove(0);
    FileInputStream fis = new FileInputStream(file);
    newSize += file.length();
    String fileName = file.getName().substring(2);
    if(newSize < MAX_SIZE || first){
        subZos.putNextEntry(new ZipEntry(pdfName+" "+fileName+".pdf"));
        IOUtils.copy(fis, subZos);
        file.delete();
        subZos.closeEntry();
        first = false;
    }else{
        subZos.flush();
        subZos.close();

        masterZos.write(subBaos.toByteArray());
        masterZos.closeEntry();
        masterZos.putNextEntry(new ZipEntry(zipName+" "+count++ +".zip"));
        subBaos = new ByteArrayOutputStream();
        subZos = new ZipOutputStream(subBaos);
        subZos.putNextEntry(new ZipEntry(pdfName+" "+fileName+".pdf"));
        IOUtils.copy(fis, subZos);
        file.delete();

        newSize = file.length();
        first = true;
    }
    subZos.closeEntry();
}
subZos.closeEntry();
subZos.flush();
subZos.close();
masterZos.write(subBaos.toByteArray());

masterZos.closeEntry();
}

Increase Java heap size by specifying VM arguments in your server configuration 通过在服务器配置中指定VM参数来增加Java堆大小

Ex: 例如:

-Xmx1024m

I you are running Tomcat in eclipse you can specify VM arguments in Open launch configuration of Server Configuration. 如果您在Eclipse中运行Tomcat,则可以在“服务器配置”的“打开启动”配置中指定VM参数。

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

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