从分块文件

时间:2016-01-16 05:31:00

标签: java spring io

当用户上传大文件(> 1 GB)(我正在使用flow.js库)时出现问题,它会在临时目录中创建数十万个小块文件(例如每个100KB)但无法合并由于MemoryOutOfException导致单个文件。当文件低于1 GB时,不会发生这种情况。我知道这听起来很乏味,你可能建议我在我的容器中增加XmX - 但除此之外我想要另一个角度。

这是我的代码

private void mergeFile(String identifier, int totalFile, String outputFile) throws AppException{
    File[] fileDatas = new File[totalFile]; //we know the size of file here and create specific amount of the array
    byte fileContents[] = null;
    int totalFileSize = 0;
    int filePartUploadSize = 0;
    int tempFileSize = 0;
    //I'm creating array of file and append the length
    for (int i = 0; i < totalFile; i++) {
        fileDatas[i] = new File(identifier + "." + (i + 1)); //indentifier is the name of the file 
        totalFileSize += fileDatas[i].length();
    }

    try {
        fileContents = new byte[totalFileSize];
        InputStream inStream;
        for (int j = 0; j < totalFile; j++) {
            inStream = new BufferedInputStream(new FileInputStream(fileDatas[j]));
            filePartUploadSize = (int) fileDatas[j].length();
            inStream.read(fileContents, tempFileSize, filePartUploadSize);
            tempFileSize += filePartUploadSize;
            inStream.close();
        }
    } catch (FileNotFoundException ex) {
        throw new AppException(AppExceptionCode.FILE_NOT_FOUND);
    } catch (IOException ex) {
        throw new AppException(AppExceptionCode.ERROR_ON_MERGE_FILE);
    } finally {
        write(fileContents, outputFile);
        for (int l = 0; l < totalFile; l++) {
            fileDatas[l].delete();
        }
    }
}

请再次显示此方法的“效率低下”...只有使用此方法无法合并的大文件,较小的一个(<1 GB)根本没问题.... 我很感激,如果你不建议我增加堆内存而不是向我展示这种方法的基本错误......谢谢......

由于

2 个答案:

答案 0 :(得分:3)

  

...告诉我这个方法的基本错误

实现缺陷是您正在创建一个字节数组(fileContents),其大小是文件总大小。如果文件总大小太大,那将导致OOME。不可避免。

解决方案 - 不要那样做!而是通过读取“块”文件并使用适当大小的缓冲区写入最终文件来“流式传输”文件。

您的代码也存在其他问题。例如,它可能会泄漏文件描述符,因为您无法确保inStream在所有情况下都已关闭。阅读“try-with-resources”构造。

答案 1 :(得分:2)

通过声明整个大小的字节数组,不必在内存中分配整个文件大小。通常在内存中构建连接文件是完全没有必要的。

只需打开目标文件的输出流,然后为要组合的每个文件打开它,只需将每个文件作为输入流读取,然后将字节写入输出流,并在完成时关闭每个文件。然后,当您完成所有操作后,关闭输出文件。缓冲区的总内存使用量将为几千字节。

另外,不要在finally块中进行I / O操作(闭包和填充除外)。

这是一个你可以玩的粗略例子。

        ArrayList<File> files = new ArrayList<>();// put your files here
        File output = new File("yourfilename");
        BufferedOutputStream boss = null;
        try 
        {
            boss = new BufferedOutputStream(new FileOutputStream(output));
            for (File file : files) 
            {
                BufferedInputStream bis = null;
                try
                {
                    bis = new BufferedInputStream(new FileInputStream(file));
                    boolean done = false;
                    while (!done)
                    {
                        int data = bis.read();
                        boss.write(data);
                        done = data < 0;
                    }
                }
                catch (Exception e)
                {
                    //do error handling stuff, log it maybe? 
                }
                finally
                {
                    try
                    {
                        bis.close();//do this in a try catch just in case
                    }
                    catch (Exception e)
                    {
                        //handle this
                    }
                }               
            }
        } catch (Exception e) 
        {
            //handle this
        }
        finally
        {
            try 
            {
                boss.close();
            } 
            catch (Exception e) {
                //handle this
            }
        }