繁体   English   中英

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

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

我想创建一个zip文件来下载它。 我有大量的pdf文件。

我需要将它们压缩为50MB的子zip,所以我创建了一个包含所有其他子zip的主zip。

但是当运行我的代码时,我得到:

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

这是我分割和压缩所有文件的代码:

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();
}

我该如何解决?

编辑:我添加了代码以将我的子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();

获取所有zip文件并将其放入我的主zip文件中,但是将它们放入主zip文件中时会遇到相同的错误...

我是这样解决的:对于主zip文件,我使用了FileOutputStream ,而对所有其他子zip文件,我使用了ByteArrayOutputStream ,并且每隔50 MB刷新并关闭它一次。

因此,我不会对每个子zip文件都使用文件,因此使用流比使用文件要快。

这是代码:

    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();
}

从代码中可以看出,您正在将每个子zip压缩到一个由ByteArrayOutput流支持的输出流中,这将消耗内存。 将每个Sub文件写入磁盘上的临时位置,然后将单个zip推入主zip会减少您的内存需求,同时写入磁盘会产生开销,但是如果总体大小未知会是更好的方法。

只要磁盘有足够的空间来处理它,就可以将临时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();
}

通过在服务器配置中指定VM参数来增加Java堆大小

例如:

-Xmx1024m

如果您在Eclipse中运行Tomcat,则可以在“服务器配置”的“打开启动”配置中指定VM参数。

暂无
暂无

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

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