package org.smartly.commons.io.filetokenizer;
import org.smartly.commons.util.FileUtils;
import org.smartly.commons.util.PathUtils;
import org.smartly.commons.util.StringUtils;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* Split file into multiple chunks.
* Join file from multiple chunks
*/
public class FileTokenizer {
public static final String CHUNK_EXT = ".chunk";
// --------------------------------------------------------------------
// c o n s t r u c t o r
// --------------------------------------------------------------------
private FileTokenizer() {
}
// --------------------------------------------------------------------
// p u b l i c
// --------------------------------------------------------------------
public static String getChunkName(final String filename, final int index){
final String name = PathUtils.getFilename(filename, true);
return name + CHUNK_EXT + "_" + index;
}
public static String[] splitFromChunkSize(final String filename,
final long chunkSize,
final IFileTokenizerCallback progressCallback) throws Exception {
return splitFromChunkSize(filename, null, chunkSize, progressCallback);
}
public static String[] splitFromChunkSize(final String filename,
final String folder,
final long chunkSize,
final IFileTokenizerCallback progressCallback) throws Exception {
final File file = new File(filename);
final FileChunkInfo info = new FileChunkInfo(file.length(), chunkSize);
return splitFile(file, folder, info, progressCallback);
}
public static String join(final String[] fileNames,
final String outputFilename,
final IFileTokenizerCallback progressCallback) throws IOException {
joinFiles(fileNames, outputFilename, progressCallback);
return outputFilename;
}
// --------------------------------------------------------------------
// p r i v a t e
// --------------------------------------------------------------------
private static String[] splitFile(final File file,
final String folder,
final FileChunkInfo info,
final IFileTokenizerCallback progressCallback) throws Exception {
final String filename = file.getAbsolutePath();
final String name = PathUtils.getFilename(filename, true);
final String root = StringUtils.hasText(folder)
? PathUtils.concat(PathUtils.getTemporaryDirectory("TOKENIZER/"), folder)
: PathUtils.getTemporaryDirectory("TOKENIZER/");
// root must exist
FileUtils.mkdirs(root);
// create file names
final String[] names = new String[info.getChunkCount()];
for (int i = 0; i < names.length; i++) {
names[i] = PathUtils.concat(root, name + CHUNK_EXT + "_" + i);
}
// creates chunks.
int terminated = 0;
for (int i = 0; i < names.length; i++) {
// create
createChunk(filename, names[i], i, info.getChunkSize());
//-- call progress --//
if (null != progressCallback) {
terminated++;
double progress = (double) terminated / (double) info.getChunkCount();
progressCallback.onProgress(terminated, info.getChunkCount(), progress);
}
}
return names;
}
private static void createChunk(final String inputFilename,
final String outputFilename,
final long index,
final long chunkSize) throws IOException {
FileOutputStream output = new FileOutputStream(outputFilename);
FileInputStream input = new FileInputStream(inputFilename);
long offset = index * chunkSize;
//long max = offset + chunkSize;
try {
input.skip(offset);
byte[] buffer = new byte[32768];
long count = 0;
while (true) {
long remaining = chunkSize - count;
int read = input.read(buffer, 0, remaining > buffer.length ? buffer.length : (int) remaining);
if (read <= 0) break;
count += read;
output.write(buffer, 0, read);
if (count >= chunkSize) break;
}
} finally {
output.flush();
output.close();
output = null;
input.close();
input = null;
}
}
private static void joinFiles(final String[] filenames,
final String outputFilename,
final IFileTokenizerCallback progressCallback) throws IOException {
FileUtils.delete(outputFilename);
FileOutputStream output = new FileOutputStream(outputFilename);
try {
//-- read all files one by one and append stream in outputFileName --//
int terminated = 0;
for (int i = 0; i < filenames.length; i++) {
FileInputStream input = new FileInputStream(filenames[i]);
try {
byte[] buffer = new byte[32768];
int read;
while ((read = input.read(buffer, 0, buffer.length)) > 0) {
output.write(buffer, 0, read);
}
} finally {
input.close();
input = null;
}
//-- call progress --//
if (null != progressCallback) {
terminated++;
double progress = (double) terminated / (double) filenames.length;
progressCallback.onProgress(terminated, filenames.length, progress);
}
}
} finally {
output.flush();
output.close();
output = null;
}
}
}