转载请注明出处!
http://blog.csdn.net/hitliuxiaodong/article/details/52268818
前几天参加酷家乐的面试,两轮技术电话面,一轮电话hr面,然后hr通知我现场面,我以为现场应该是hr面,结果去了让写一个文件分割器。写了三个多小时,发现文件可以被成功分割,可是合并的时候一直出问题,然后面试好像就gg了(—–分割— 最新情況,給offer了 )。回来不甘心,重新写了一下,发现RandomAccessFile 的用法写错了。怪不得一直出现越界错误。(使用RandomAccessFile 写文件时需要指定文件大小)
题目是:实现一个文件分割合并器,可以对任意文件分割指定的n份到一个目录下,同时分割后的文件名称可能被修改,所以不能依靠文件名称进行合并。
所以我想的是 以二进制流的形式进行读取,然后分割为n份后,在每一份文件的头部添加文件信息,然后再写入每一块小文件,合并时就可以根据文件的头部信息进行校验合并。
首先是一个工具类ByteUtils.java ,可以进行int 转bytes 和 bytes转int
package com.xiaodongliu;
import java.nio.ByteBuffer;
/**
* Created by lxd on 16-8-20.
*/
public class ByteUtils {
public static void setIntBytes(byte[] dst, int offset, int value) {
dst[offset] = (byte) (value & 0xff);// 最低位
dst[offset + 1] = (byte) ((value >> 8) & 0xff);// 次低位
dst[offset + 2] = (byte) ((value >> 16) & 0xff);// 次高位
dst[offset + 3] = (byte) (value >>> 24);// 最高位,无符号右移。
}
public static int getIntFromBytes(byte[] src, int offset) {
int targets = (src[offset] & 0xff) | ((src[offset + 1] << 8) & 0xff00) // | 表示安位或
| ((src[offset + 2] << 24) >>> 8) | (src[offset + 3] << 24);
return targets;
}
}
然后是一个文件信息的类 FileBlockInfo.java
package com.xiaodongliu;
/**
* Created by lxd on 16-8-20.
*/
public class FileBlockInfo {
private String fileName;//文件名
private int fileNameLength;//文件名长度
private int offset;
private int blockIndex;//第几块
private int allBlockCount;//文件总分数
private int blockSize;//该文件块长度
private int allSize;//文件块总长度
private byte[] data;//该块数据
public FileBlockInfo(byte[] header) {
fileNameLength = ByteUtils.getIntFromBytes(header, 0);
offset = ByteUtils.getIntFromBytes(header, 4);
blockIndex = ByteUtils.getIntFromBytes(header, 8);
allBlockCount = ByteUtils.getIntFromBytes(header, 12);
blockSize = ByteUtils.getIntFromBytes(header, 16);
allSize = ByteUtils.getIntFromBytes(header, 20);
System.out.println(toString());
public byte[] getIntegrateData() {
byte[] integeratedData = new byte[24 + fileNameLength + blockSize];
ByteUtils.setIntBytes(integeratedData, 0, fileNameLength);
ByteUtils.setIntBytes(integeratedData, 4, offset);
ByteUtils.setIntBytes(integeratedData, 8, blockIndex);
ByteUtils.setIntBytes(integeratedData, 12, allBlockCount);
ByteUtils.setIntBytes(integeratedData, 16, blockSize);
ByteUtils.setIntBytes(integeratedData, 20, allSize);
System.arraycopy(fileName.getBytes(), 0, integeratedData, 24, fileNameLength);
System.arraycopy(data, 0, integeratedData, 24 + fileNameLength, blockSize);
return integeratedData;
}
@Override
public String toString() {
return "FileBlockInfo{" +
"fileNameLength=" + fileNameLength +
", offset=" + offset +
", blockIndex=" + blockIndex +
", allBlockCount=" + allBlockCount +
", blockSize=" + blockSize +
", allSize=" + allSize +
'}';
}
public byte[] getData() {
return data;
}
public void setData(byte[] data) {
this.data = data;
}
public FileBlockInfo() {
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
public int getFileNameLength() {
return fileNameLength;
}
public void setFileNameLength(int fileNameLength) {
this.fileNameLength = fileNameLength;
}
public int getOffset() {
return offset;
}
public void setOffset(int offset) {
this.offset = offset;
}
public int getBlockIndex() {
return blockIndex;
}
public void setBlockIndex(int blockIndex) {
this.blockIndex = blockIndex;
}
public int getAllBlockCount() {
return allBlockCount;
}
public void setAllBlockCount(int allBlockCount) {
this.allBlockCount = allBlockCount;
}
public int getBlockSize() {
return blockSize;
}
public void setBlockSize(int blockSize) {
this.blockSize = blockSize;
}
public int getAllSize() {
return allSize;
}
public void setAllSize(int allSize) {
this.allSize = allSize;
}
}
接下来就是实现的分割功能 Splitter.java
package com.xiaodongliu;
import java.io.*;
/**
* Created by lxd on 16-8-20.
*/
public class Splitter {
private String sourceFile;
private int blockNum;
private String dstPath;
private String fileName;
public Splitter(String sourceFile, int blockNum, String dstPath) {
this.sourceFile = sourceFile;
this.blockNum = blockNum;
this.dstPath = dstPath;
this.fileName = this.sourceFile.substring(this.sourceFile.lastIndexOf("/")+ 1);
}
public void StartSplit() {
File file = new File(sourceFile);
if (file.exists()) {
int fileSize = (int) file.length();
int pieceSize = (int) (fileSize / blockNum);
if ((fileSize - pieceSize * blockNum) != 0) {
pieceSize = (int) (fileSize / (blockNum - 1));
}
byte[] data = new byte[pieceSize];
FileInputStream fileInputStream = null;
File dstFold=new File(dstPath);
if(!dstFold.exists()){
dstFold.mkdirs();
}
try {
fileInputStream = new FileInputStream(file);
int realSize = 0;
int offset = 0;
int blockIndex = 0;
while ((realSize = fileInputStream.read(data, 0, pieceSize)) != -1) {
FileBlockInfo fileBlockInfo = new FileBlockInfo();
fileBlockInfo.setFileName(fileName);
fileBlockInfo.setFileNameLength(fileName.getBytes().length);
fileBlockInfo.setOffset(offset);
fileBlockInfo.setBlockIndex(blockIndex);
fileBlockInfo.setAllBlockCount(blockNum);
fileBlockInfo.setBlockSize(realSize);
fileBlockInfo.setAllSize(fileSize);
fileBlockInfo.setData(data);
data = new byte[pieceSize];
blockIndex++;
offset += realSize;
new WriteAsync(dstPath + blockIndex, fileBlockInfo.getIntegrateData()).start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
class WriteAsync extends Thread {
private String fileName;
private byte[] data;
public WriteAsync(String fileName, byte[] data) {
this.fileName = fileName;
this.data = data;
}
@Override
public void run() {
File file = new File(fileName);
if (!file.exists()) {
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
FileOutputStream fileOutputStream = null;
try {
fileOutputStream = new FileOutputStream(file);
fileOutputStream.write(data);
fileOutputStream.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
System.out.println("第" + fileName + "份写入完成");
}
}
}
以及实现合并功能的类Merger.java
package com.xiaodongliu;
import java.io.*;
/**
* Created by lxd on 16-8-20.
*/
public class Merger {
private String sourcePath;
private String destinyPath;
private String fileName;
public Merger(String sourcePath, String desPath) {
this.sourcePath = sourcePath;
this.destinyPath = desPath;
}
public void StartMerge() {
File file = new File(sourcePath);
if (file.isDirectory()) {
File[] files = file.listFiles();
FileBlockInfo fileBlockInfo;
for (int i = 0; i < files.length; i++) {
FileInputStream fileInputStream = null;
try {
fileInputStream = new FileInputStream(files[i]);
byte[] fileInfoData = new byte[24];
fileInputStream.read(fileInfoData, 0, 24);
fileBlockInfo = new FileBlockInfo(fileInfoData);
byte[] fileNameData = new byte[fileBlockInfo.getFileNameLength()];
fileInputStream.read(fileNameData, 0, fileNameData.length);
if (i == 0){
fileName = destinyPath + new String(fileNameData);
System.out.println("文件名--》"+fileName);
RandomAccessFile randomAccessFile=new RandomAccessFile(fileName,"rw");
randomAccessFile.setLength(fileBlockInfo.getAllSize());
randomAccessFile.close();
}
byte[] realData = new byte[fileBlockInfo.getBlockSize()];
fileInputStream.read(realData, 0, realData.length);
new MultiMergeThread(fileName, fileBlockInfo.getOffset(), realData).start();
} catch (IOException e) {
e.printStackTrace();
}
}
System.out.println("合并完成");
}
}
class MultiMergeThread extends Thread {
private long offset;
private byte[] data;
private String fileName;
public MultiMergeThread(String fileName, long offset, byte[] data) {
this.offset = offset;
this.data = data;
this.fileName = fileName;
}
@Override
public void run() {
RandomAccessFile randomAccessFile = null;
try {
randomAccessFile = new RandomAccessFile(fileName, "rw");
randomAccessFile.seek(offset);
randomAccessFile.write(data);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
randomAccessFile.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
最后就是两个测试类SpliterMain.java
package com.xiaodongliu;
import java.util.Scanner;
/**
* Created by lxd on 16-8-20.
*/
public class SpliterMain {
public static void main(String[] args){
Scanner cin=new Scanner(System.in);
String sourceFile = null;
int blockNum = 0;
String dstFolder = null;
System.out.println("请输入文件全路径名...");
while (cin.hasNext()){
sourceFile=cin.nextLine();
System.out.println("请输入需要分割的分数...");
blockNum=cin.nextInt();
cin.nextLine();
System.out.println("请输入目标路径...");
dstFolder=cin.nextLine();
Splitter splitter=new Splitter(sourceFile,blockNum,dstFolder);
splitter.StartSplit();
System.out.println("请输入文件全路径名...");
}
}
}
MegerMain.java
package com.xiaodongliu;
import java.util.Scanner;
/**
* Created by lxd on 16-8-20.
*/
public class MegerMain {
public static void main(String[] args) {
Scanner cin = new Scanner(System.in);
String sourceFile = null;
int blockNum = 0;
String dstFolder = null;
System.out.println("请输入文件全路径名...");
while (cin.hasNext()) {
sourceFile = cin.nextLine();
System.out.println("请输入目标路径...");
dstFolder = cin.nextLine();
Merger merger = new Merger(sourceFile, dstFolder);
merger.StartMerge();
System.out.println("请输入文件全路径名...");
}
}
}
源码在 基于java的文件分割器源码