当用户上传大文件(> 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)根本没问题.... 我很感激,如果你不建议我增加堆内存而不是向我展示这种方法的基本错误......谢谢......
由于
答案 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
}
}