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

时间:2014-05-29 09:58:38

标签: java spring zip

我想创建一个zip文件来下载它。我有很多pdf文件..

我需要将它们压缩成50MB的子拉链,所以我创建了一个包含所有其他子拉链的主拉链..

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

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

我该如何解决?

修改 我添加了代码以保存在tmp文件夹中我的子zip文件..而且我这样做:

    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文件,但是当我将它们放入其中时我得到了相同的错误...

3 个答案:

答案 0 :(得分:4)

我这样解决了: 对于主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();
}

答案 1 :(得分:2)

从代码中可以看出,您正在将每个子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();
}

答案 2 :(得分:0)

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

例如:

-Xmx1024m

我在eclipse中运行Tomcat,你可以在其中指定VM参数 打开服务器配置的启动配置。