import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class ChunksCenter {
private volatile static Map<String, Item> chunkMap = new HashMap<String, Item>();
private static volatile ChunksCenter instance = null;
private static volatile Object object = new Object();
private ChunksCenter() {
super();
}
private static ChunksCenter getInstance() {
if (instance == null) {
synchronized (ChunksCenter.class) {
if (instance == null) {
instance = new ChunksCenter();
}
}
}
return instance;
}
/**
* 存放分片文件
* @param key
* @param chunkIndex --- 分片索引
* @param file --- 分片文件
* @throws Exception
*/
public static void putFile(String key, int chunkIndex, File file) throws Exception {
if(key == null) {
throw new NullPointerException("key is null, can update the key's file");
}
Item item = getItem(key);
if(item == null) {
throw new Exception("key: " + key + "has not inited!");
} else {
item.putFile(chunkIndex, file);
}
}
/**
* 初始化item存放该key对应的item信息
* @param key
* @param finalFile --- 最终文件
* @param filename --- 文件名
* @param total --- 分片总个数
* @param chunkSize --- 分片大小(平均)
* @param fileSize --- 文件总大小
* @throws Exception
*/
public static void initItem(String key, File finalFile, String filename, long total, long chunkSize, long fileSize) throws Exception {
synchronized (object) {
Item item = getItem(key);
if(item == null) {
if(chunkMap.get(key) != null) {
throw new Exception("key: " + key + " has inited!");
}
item = getInstance().new Item(finalFile, filename, total, fileSize, chunkSize);
chunkMap.put(key, item);
}
}
}
/**
* 根据key获取对应的item
* @param key
* @return
*/
public static ChunksCenter.Item getItem(String key) {
Item item = null;
if(key != null) {
item = chunkMap.get(key);
}
return item;
}
/**
* 根据key判断是否已初始化
* @param key
* @return
*/
public static boolean hasInited(String key) {
return getItem(key) != null;
}
/**
* 销毁该key的信息
* @param key
* @param delete --- 是否删除文件
*/
public static void destroy(String key, boolean delete) {
if(key != null) {
Item item = getItem(key);
if(item != null && delete) {
item.deleteFiles();
}
chunkMap.remove(key);
}
}
public class Item {
private Map<Integer, File> filesMap = new HashMap<Integer, File>();
private File finalFile; //最终文件
private String filename; //文件名
private long total; //分片总数量
private long fileSize; //文件大小
private long chunkSize; //平均分片大小
private Item(File finalFile, String filename, long total, long fileSize, long chunkSize) {
super();
this.finalFile = finalFile;
this.filename = filename;
this.total = total;
this.fileSize = fileSize;
this.chunkSize = chunkSize;
}
private synchronized void putFile(Integer index, File file) {
if(file != null && file.exists()) { //当文件存在时
filesMap.put(index, file);
}
}
private void deleteFiles() {
if(filesMap != null) {
for(File file : filesMap.values()) {
if(file.exists()) {
file.delete();
}
}
}
}
public File getFile(Integer index) {
return filesMap.get(index);
}
public List<File> getFiles() {
return new ArrayList<File>(filesMap.values());
}
public List<File> getFilesSortByIndex() {
List<File> files = new ArrayList<File>();
if(filesMap != null) {
for (int i = 0; i < total; i++) {
files.add(filesMap.get(i));
}
}
return files;
}
/**
* 判断map中文件个数是否与总个数相等
* @return
*/
public boolean hasComplete() {
return filesMap.size() == total;
}
public File getFinalFile() {
return finalFile;
}
public String getFilename() {
return filename;
}
public long getTotal() {
return total;
}
public long getFileSize() {
return fileSize;
}
public long getChunkSize() {
return chunkSize;
}
}
}
初始化 (当是分片上传时,判断key是否已经初始化,如果没有则进行初始化数据)
if(!ChunksCenter.hasInited(key)) { //判断key是否已初始化,无需判断是第几个分片,直接使用
//finalFile为最终保存的文件
ChunksCenter.initItem(key, finalFile, filename, total, chunkSize, fileSize);
}
放入分片文件数据 (需要在初始化key后使用)
ChunksCenter.putFile(key, chunkIndex, file); //chunkIndex 第几个分片文件, file 分片文件
获取key对应的item数据 (推荐在判断分片索引是否为最后一个时使用)
ChunksCenter.Item item = ChunksCenter.getItem(key);
boolean result = item.hasComplete(); //判断是否完成,依据分片个数和与分片总个数是否相等
File finalFile = item.getFinalFile(); //获取最终文件
List<File> files = item.getFilesSortByIndex(); //获取按分片索引顺序的文件集合
销毁key数据
ChunksCenter.destroy(key, flag); //flag为true时删除对应key的item所记录的文件
其他: 使用junit模拟储存本地分片文件
@org.junit.Test
public void test2() throws IOException, IllegalAccessException {
File srcFile = new File("d:/迅雷下载/天将雄师BD中字1.rmvb"); //原文件
final File lastFile = new File("d:/迅雷下载/天将雄师BD中字1-thread.rmvb"); //最终的文件
File dir = new File("d:/迅雷下载/天将雄师BD中字1"); //存放分片文件的文件夹
File[] dirFiles = dir.listFiles(); //分片文件
final int chunksNum = dirFiles.length; //分片总个数
final String key = System.currentTimeMillis() + "";
final String filename = "天将雄师BD中字1-thread.rmvb";
final long size = srcFile.length(); //文件大小
final long avg = size / chunksNum; //分片平均大小
Map<Integer, File> fileMap = new HashMap<Integer, File>();
int index = 0;
for(File file : dirFiles) {
fileMap.put(index ++, file); //存放文件的map
}
final CountDownLatch doneSignal = new CountDownLatch(chunksNum); //让主线程等待所有子线程执行完毕后使用的类
Integer[] indexArr = random(chunksNum, chunksNum, true); //生成不重复的随机数 0-4
for (int i = 0; i < chunksNum; i++) {
final Integer chunkIndex = indexArr[i];
final File file = fileMap.get(chunkIndex);
try {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
try {
if (!ChunksCenter.hasInited(key)) {
ChunksCenter.initItem(key, lastFile, filename, chunksNum, avg, size);
}
ChunksCenter.putFile(key, chunkIndex, file);
doneSignal.countDown(); //current thread finished! noted by latch object!
} catch (Exception e) {
e.printStackTrace();
}
}
});
thread.start();
} catch (Exception e) {
e.printStackTrace();
}
}
//确认所有线程任务完成,开始执行主线程的操作
try {
doneSignal.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
ChunksCenter.Item item = ChunksCenter.getItem(key);
if (item != null && item.hasComplete()) {
List<File> files = item.getFilesSortByIndex();
System.out.println(files);
//合并文件?...
}
}
//生成随机数
public Integer[] random(int randomVal, int arrSize, boolean startZero) throws IllegalAccessException {
if(randomVal < arrSize) {
throw new IllegalAccessException("randomVal should more than arrSize");
} else if (randomVal <= 0) {
throw new IllegalAccessException("randomVal should more than 0");
}
Integer[] arr = new Integer[arrSize];
for (int i = 0; true;) {
int val = new Random().nextInt(randomVal);
if(!startZero) {
val += 1;
}
boolean hasExist = false;
if(i > 0) {
for(int j = i - 1; j >= 0; j --) {
if (val == arr[j]) {
hasExist = true;
break;
}
}
}
if(!hasExist) {
arr[i ++] = val;
}
if(i == arrSize) {
break;
}
}
return arr;
}
其他: 分割文件方法
/**
* 分割文件到指定文件夹,分割文件名称为源文件名称前缀+tailContent+索引+后缀
* @param srcFile --- 要分割的文件
* @param outFileDirectory --- 输出文件夹路径
* @param deleteOutFileDirectory --- 是否删除已存在的输出文件夹
* @param tailContent --- 在后缀前的内容
* @param num --- 分割的个数
* @throws FileNotFoundException
* @throws IOException
*/
public static void splitFile(File srcFile, String outFileDirectory,
boolean deleteOutFileDirectory, String tailContent,
int num) throws FileNotFoundException, IOException {
RandomAccessFile inAccessFile = null;
if(!srcFile.exists()) {
throw new IllegalArgumentException("srcFile is not exist");
} else if(!srcFile.isFile()) {
throw new IllegalArgumentException("srcFile is not file");
} else if(outFileDirectory == null) {
throw new NullPointerException("outFileDirectory must be not null");
}
try {
long totalSize = srcFile.length();
long avgSize = totalSize / num;
inAccessFile = new RandomAccessFile(srcFile, "r");
String filename = srcFile.getName();
int index = filename.lastIndexOf(".");
if(tailContent == null) {
tailContent = "";
}
String prefix = filename;
String suffix = "";
if(index >= 0) {
prefix = prefix.substring(0, index);
suffix = filename.substring(index);
}
File fileDirectory = new File(outFileDirectory);
if(deleteOutFileDirectory && fileDirectory.exists() && fileDirectory.isDirectory()) { //删除输出的文件夹
deleteDir(fileDirectory);
}
if(!fileDirectory.exists()) {
fileDirectory.mkdirs();
}
byte[] bytes = new byte[1024];
int len = -1;
for (int i = 0; i < num; i++) {
File outFile = new File(fileDirectory,prefix + tailContent + (i + 1) + suffix);
RandomAccessFile outAccessFile = new RandomAccessFile(outFile, "rw");
while ((len = inAccessFile.read(bytes)) != -1) {
outAccessFile.write(bytes, 0, len);
if(i != num - 1) {
if(outAccessFile.length() > avgSize) {
break;
}
}
}
outAccessFile.close();
}
} finally {
if(inAccessFile != null) {
inAccessFile.close();
}
}
}
/**
* 删除文件夹 --- IOGroovyMethods
* @param self
* @return
*/
public static boolean deleteDir(File self) {
if(!self.exists()) {
return true;
} else if(!self.isDirectory()) {
return false;
} else {
File[] files = self.listFiles();
if(files == null) {
return false;
} else {
boolean result = true;
File[] arr$ = files;
int len$ = files.length;
for(int i$ = 0; i$ < len$; ++i$) {
File file = arr$[i$];
if(file.isDirectory()) {
if(!deleteDir(file)) {
result = false;
}
} else if(!file.delete()) {
result = false;
}
}
if(!self.delete()) {
result = false;
}
return result;
}
}
}