Write new files from a Stream at first to a .tmp file. This avoids fragmentation.

This commit is contained in:
Maschell 2018-12-06 15:40:05 +01:00
parent 2479572917
commit 51140fb7c7
3 changed files with 152 additions and 6 deletions

View File

@ -0,0 +1,8 @@
package de.mas.wiiu.jnus.utils;
import java.io.IOException;
@FunctionalInterface
public interface CheckedFunction<T> {
void apply(T t) throws IOException;
}

View File

@ -20,6 +20,8 @@ import java.io.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import lombok.NonNull; import lombok.NonNull;
@ -55,16 +57,41 @@ public final class FileUtils {
return true; return true;
} }
public static void saveInputStreamToFile(@NonNull File outputFile, InputStream inputStream, long filesize) throws IOException {
FileAsOutputStreamWrapper(outputFile, filesize, outputStream -> StreamUtils.saveInputStreamToOutputStream(inputStream, outputStream, filesize));
}
/** /**
* Saves a byte array to a file (and overwrite it if its already exists) DOES NOT IF THE PATH/FILE EXIST OR IS IT EVEN A FILE * Allows to write into a target file as OutputStream with some extras. The provided OutputStream already has the needed memory allocated. This results in a
* non-fragmented file.
* *
* @param output * @param outputFile
* @param inputStream * @param filesize
* @param action
* @throws IOException * @throws IOException
*/ */
public static void saveInputStreamToFile(@NonNull File output, InputStream inputStream, long filesize) throws IOException { public static void FileAsOutputStreamWrapper(@NonNull File outputFile, long filesize, CheckedFunction<OutputStream> action) throws IOException {
FileOutputStream out = new FileOutputStream(output); // Create a new temp file which already has the target filesize allocated.
StreamUtils.saveInputStreamToOutputStream(inputStream, out, filesize); String tempFilePath = outputFile.getAbsolutePath() + "." + outputFile.getAbsolutePath().hashCode() + ".part";
File tempFile = new File(tempFilePath);
if (tempFile.exists()) {
tempFile.delete();
}
tempFile.createNewFile();
RandomAccessFile outStream = new RandomAccessFile(tempFilePath, "rw");
outStream.setLength(filesize);
outStream.seek(0L);
action.apply(new RandomFileOutputStream(outStream));
outStream.close();
// Rename temp file.
if (outputFile.exists()) {
outputFile.delete();
}
tempFile.renameTo(outputFile);
} }
} }

View File

@ -0,0 +1,111 @@
package de.mas.wiiu.jnus.utils;
import java.io.File;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.OutputStream;
import java.io.RandomAccessFile;
public class RandomFileOutputStream extends OutputStream {
// *****************************************************************************
// INSTANCE PROPERTIES
// *****************************************************************************
protected RandomAccessFile randomFile; // the random file to write to
protected boolean sync; // whether to synchronize every write
// *****************************************************************************
// INSTANCE CONSTRUCTION/INITIALIZATON/FINALIZATION, OPEN/CLOSE
// *****************************************************************************
public RandomFileOutputStream(String fnm) throws IOException {
this(fnm, false);
}
public RandomFileOutputStream(String fnm, boolean syn) throws IOException {
this(new File(fnm), syn);
}
public RandomFileOutputStream(File fil) throws IOException {
this(fil, false);
}
public RandomFileOutputStream(RandomAccessFile ran) throws IOException {
randomFile = ran;
sync = false;
}
public RandomFileOutputStream(File fil, boolean syn) throws IOException {
super();
File par; // parent file
fil = fil.getAbsoluteFile();
if ((par = fil.getParentFile()) != null) {
Utils.createDir(par.getAbsolutePath());
}
randomFile = new RandomAccessFile(fil, "rw");
sync = syn;
}
// *****************************************************************************
// INSTANCE METHODS - OUTPUT STREAM IMPLEMENTATION
// *****************************************************************************
public void write(int val) throws IOException {
randomFile.write(val);
if (sync) {
randomFile.getFD().sync();
}
}
public void write(byte[] val) throws IOException {
randomFile.write(val);
if (sync) {
randomFile.getFD().sync();
}
}
public void write(byte[] val, int off, int len) throws IOException {
randomFile.write(val, off, len);
if (sync) {
randomFile.getFD().sync();
}
}
public void flush() throws IOException {
if (sync) {
randomFile.getFD().sync();
}
}
public void close() throws IOException {
randomFile.close();
}
// *****************************************************************************
// INSTANCE METHODS - RANDOM ACCESS EXTENSIONS
// *****************************************************************************
public long getFilePointer() throws IOException {
return randomFile.getFilePointer();
}
public void setFilePointer(long pos) throws IOException {
randomFile.seek(pos);
}
public long getFileSize() throws IOException {
return randomFile.length();
}
public void setFileSize(long len) throws IOException {
randomFile.setLength(len);
}
public FileDescriptor getFD() throws IOException {
return randomFile.getFD();
}
} // END PUBLIC CLASS