Java 分割功能实现

    在项目针对比较大的文件需要文件的分割功能,特意写了一个具有分割功能代码,以供大家拍砖之用,目的进行沟通交流,如有好的建议和联系本人沟通交流谢谢!

package easyway.tbs.file.transport.core.split;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.io.Serializable;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * 文件读写访问器
 * 
 * @author longgangbai
 *
 */
public class DataFileAccessor implements Serializable {
	 private final static Log logger = LogFactory.getLog(DataFileAccessor.class);
     
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	/**
	 * 只读模式
	 */
	public static final String RANDOMACESSFILE_READ_MODE="r";
	/**
	 * 只写模式
	 */
	public static final String RANDOMACESSFILE_WRITE_MODE="w";
	/**
	 * 读写模式
	 */
	public static final String RANDOMACESSFILE_WRITE_READ_MODE="rw";
	
	/**
	 * 随机读取的缓存文件的长度
	 */
	public static final int RANDOMACESSFILE_BUFFER_LENGTH=1024*1000;
				
				
	/**
	 * 文件读取的对象操作对象
	 */
	private RandomAccessFile fileAcessor;
	/**
	 * 在文件的路径
	 */
    private String filePath;
    
    /**
     * 
     * @throws IOException
     */
	public DataFileAccessor() throws IOException {
		this("", RANDOMACESSFILE_WRITE_READ_MODE);
	}

	/**
	 * 文件读取访问其
	 * @param sName 
	 * @param nPos
	  * @param nPos
	 * @throws IOException
	 */
	public DataFileAccessor(String filePath, String mode) throws IOException {
		this.filePath=filePath;
		//支持读写操作
		if(logger.isDebugEnabled()){
			logger.info("filePath="+filePath+"======mode===="+mode);
		}
		fileAcessor = new RandomAccessFile(filePath, mode);
	}

	/**
	 * 写文件的片段
	 * @param buffer 
	 *             临时存储的数据的数组
	 * @param nStart
	 * @param nLen
	 * @return
	 */
	public synchronized int write(byte[] buffer, int nStart, int nLen) {
		if(logger.isDebugEnabled()){
			logger.info("nStart="+nStart+"  nLen="+nLen);
		}
		int n = -1;
		try {
			//移动文件指针
			fileAcessor.seek(nStart);
			fileAcessor.write(buffer, nStart, nLen);
			n = nLen;
		} catch (IOException e) {
			logger.error("write file "+filePath +" nStart="+nStart+"   nLen="+nLen,e);
			
		}
		return n;
	}
	
	/**
	 * 写文件的片段
	 * @param buffer 
	 *             临时存储的数据的数组
	 * @param nStart
	 * @param nLen
	 * @return
	 */
	public synchronized int readFile(byte[] buffer, int offset, int nLen) {
		try {
			fileAcessor.seek(offset);
			long length = nLen + 1;// 文件段的长度
			byte[] b = new byte[RANDOMACESSFILE_BUFFER_LENGTH];
			long m = length / b.length;// 读和写入2048个字节的次数
			int leftsize = (int) (length - m * b.length);// 剩下的字节数
			long timestart = System.currentTimeMillis();
			for (long i = 1; i <= m; i++) {
				fileAcessor.read(b);// 将数据从from文件读入
			}
			fileAcessor.read(b, 0, leftsize);// 读入最后剩下的字节
			fileAcessor.close();// 关闭输入
			long timeend = System.currentTimeMillis();
			if(logger.isDebugEnabled()){
				logger.info("线程" + Thread.currentThread().getName()
						+ "读写完毕,共使用" + (timeend - timestart) + "毫秒");
			}
			
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		return -1;
	}
	/**
	 * 
	 * @param file
	 * @param tempFile
	 * @param begin
	 * @param end
	 * @return
	 * @throws IOException
	 */
	  public  long writeTempFile(File file, File tempFile, long begin ,long end) throws IOException{
		  RandomAccessFile in=new RandomAccessFile(file,RANDOMACESSFILE_READ_MODE);
		  RandomAccessFile out=new RandomAccessFile(tempFile,RANDOMACESSFILE_WRITE_READ_MODE);
		  byte[] b=new byte[RANDOMACESSFILE_BUFFER_LENGTH];
		  int n=0;
		  in.seek(begin);
		  while(in.getFilePointer()<=end&&(n=in.read(b))!=-1){
			  out.write(b,0,n);
		  }
		  long endPointer=in.getFilePointer();
		  in.close();
		  out.close();
		  return endPointer;
	  }
	  
	
	public synchronized int read(byte[] buffer, int offset, int nLen) {
		int n = -1;
		FileInputStream is=null;
		try {
			 is=new FileInputStream(filePath);
			 //忽略前面的读取信息
			 is.skip(offset);
			 fileAcessor.seek(offset);
			 //定义读取输入流内容的缓存数据
			 byte[] buff=new byte[RANDOMACESSFILE_BUFFER_LENGTH];
			 //定义最多需要读取几次就可以完成本次的读取
			 long times=(nLen%RANDOMACESSFILE_BUFFER_LENGTH==0)?nLen/RANDOMACESSFILE_BUFFER_LENGTH:((nLen/RANDOMACESSFILE_BUFFER_LENGTH)+1);
			 int hasRead=0;
			 for (int i = 0; i < times; i++) {
				 hasRead=is.read(buff);
				 if(hasRead<0){
					 break;
				 }
				 fileAcessor.read(buffer);
			}
			n = nLen;
		} catch (IOException e) {
			e.printStackTrace();
		}finally{
			if(is!=null){
				try {
					is.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
				is=null;
			}
		}
		return n;
	}
	
	/**
	 * 关闭流对象
	 *
	 */
	public void close(){
	    if (fileAcessor != null) {
            try {
            	fileAcessor.close();
            } catch (Throwable ignore) {
            }
            fileAcessor = null;
        }
	}


	public String getFilePath() {
		return filePath;
	}

	public void setFilePath(String filePath) {
		this.filePath = filePath;
	}
	
	
}

 

 

package easyway.tbs.file.transport.core.split;

import java.io.File;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;

import org.apache.commons.io.FileUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
 * 文件分割线程
 * @author longgangbai
 *
 */
public class FileSplitterFetch implements Runnable {
	 private final static Log logger = LogFactory.getLog(DataFileAccessor.class);
	  
    /**
     * 将读取到的字节输出到raf中
     */
    private DataFileAccessor raf;
    /**
     * 文件整个完成标识
     */
	private CountDownLatch masterLatch;
    /**
     * 当前文件读取执行完毕的标识
     */
	private CountDownLatch childrenLatch;
    /**
     * 文件分割的文件片段
     */
    private FileSplitterMessage message;
    
    

    /**
     * 构造器 
     * @param filepath 	服务器文件的路径
     * @param offset	输出流和读取起始点
     * @param length   输出流和读取结束点
     * @param buffer   数组的大小
     * @throws IOException
     */
    public FileSplitterFetch(FileSplitterMessage message,CountDownLatch masterLatch,CountDownLatch childrenLatch) throws IOException {
    	super();
    	this.message=message;
    	this.masterLatch=masterLatch;
    	this.childrenLatch=childrenLatch;
    	this.raf=new DataFileAccessor(message.getFilePath(),DataFileAccessor.RANDOMACESSFILE_READ_MODE);
    	System.out.println("raf=="+this.raf);
    }

    /**
     * 执行读取的方法
     */
    public void run() {

    	File tempFile = message.getTempFile();
    	try {
        	if(!tempFile.exists()){
        		tempFile.createNewFile();
        	}
			//masterLatch.await();
    		this.raf.writeTempFile(new File(message.getFilePath()), tempFile, message.getStartPos(), message.getEndPos());
        } catch (Exception ex) {
        	if(tempFile.exists()){
        		try {
					FileUtils.forceDelete(tempFile);
				} catch (IOException e) {
					
				}
        	}
        }finally {  
        	//使用finally块来关闭当前线程的输入流、输出流
        	this.raf.close();
        	//childrenLatch.countDown();
        }
        
    }


}

   

 

 

package easyway.tbs.file.transport.core.split;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 文件分割管理器
 * 当单个线程执行完毕之后,开始发送文件
 * @author longgangbai
 *
 */
public class FileSplitterManager {
	private static String File_Split_Separator="_";
	 private static final transient Logger logger = LoggerFactory.getLogger(FileSplitterManager.class);

	/**
	 * 文件的总长度
	 */
	private long fileLength;

	/**
	 * 整个文件分割完成的标识
	 * 开始执行发送消息
	 */
	private CountDownLatch masterLatch = new CountDownLatch(1);
	/**
	 *文件分割线程的数量
	 */
	private int splitThreadNum;
    /**
     * 
     * 当每一个线程执行完毕时候,则报告执行执行线程完成,当所有的执行线程
     * 执行完毕,则开始发送文件
     */
	private CountDownLatch childrenLatch = null;
	
	/**
	 * 文件片段的集合
	 */
	private List<FileSplitterMessage> fileSplitterMessageList=null;
	/**
	 * 创建文件锁
	 */
	private FileTransportLock fileLock=null;
	//发送文件的路径
	private String filepath;
	//发送文件的限制大小
	private int filepageSize;
	
	
	private String fileChannel;
	
	private File file;
	
	/**
	 * 
	 * @param filepath
	 * @param filepageSize
	 */
	public FileSplitterManager(String filepath,int filepageSize,String fileChannel){
		this.filepath=filepath;
		this.filepageSize=filepageSize;
		this.fileChannel=fileChannel;
	}

	/**
	 * 文件分割管理器的方法
	 *
	 */
	public void splitFileMgr(){
		file=new File(filepath);
		 //获取文件的大小
    	long fileTotalSize=file.length();
    	//如果文件的大小
    	if(filepageSize>0&&(filepageSize<fileTotalSize)){
    		initSplitFileMgr(file);
    	}else{
    		//直接发送文件
				fileSplitterMessageList=new ArrayList<FileSplitterMessage>(1);
				
				FileSplitterMessage fileSplitMessage=new  FileSplitterMessage();
	    		fileSplitMessage.setFilePath(file.getAbsolutePath());
	    		fileSplitMessage.setFileLength(file.length());
	    		fileSplitMessage.setFileName(file.getName());
	    		fileSplitMessage.setSplitThreadNum(1);
	    		fileSplitMessage.setCurrentThreadNum(1);
	    		fileSplitMessage.setStartPos(0);
	    		fileSplitMessage.setEndPos(filepageSize);
	    		fileSplitMessage.setLen(fileTotalSize);
	    		
	    		String threadNum="1";
	    		String currentThread="1";
	    		//临时文件的命名规则:源文件名称+“_”+"总线程数"+“_”+"当前线程编号"
	    		String tempFileName=file.getName()+File_Split_Separator+threadNum+File_Split_Separator+currentThread+".tmp";
	    		//文件相对比较小,文件的总线程数为1,当前线程也为1
	    		File tempFile=getTempFileName(file.getParent(),tempFileName);
	    		
	    		fileSplitMessage.setTempFile(tempFile);
	    		fileSplitterMessageList.add(fileSplitMessage);
	    		
	    		//重新命名原来的文件,标识文件已经开始发送
	    		file.renameTo(new File(file.getAbsolutePath()+".bak"));
	    		
	    		if(logger.isDebugEnabled()){
					logger.debug("the direct send file "+file.getAbsolutePath());
				}
    	}
    	System.out.println("fileSplitterMessageList ="+fileSplitterMessageList.size());
	}
	/**
	 * 创建临时文件
	 * @param parentPath
	 * @param tempFileName
	 * @return
	 */
	public File getTempFileName(String parentPath,String tempFileName){
		System.out.println(parentPath+File.separator+tempFileName);
			File tempFile=new File(parentPath+File.separator+tempFileName);
	
		try {
			if(tempFile.exists()){
				FileUtils.forceDelete(tempFile);
				if(logger.isDebugEnabled()){
					logger.info("文件存在,已经删除!");
				}
			}
			tempFile.createNewFile();
		} catch (IOException e) {
			if(logger.isDebugEnabled()){
				logger.info("文件创建失败:"+e);
			}
		}
		return tempFile;
	}

	/**
	 * 分割文件的方法
	 *
	 */
	public void initSplitFileMgr(File file) {
		if(file.exists()){
			//创建文件锁
			//fileLock=new FileTransportLock(file,false);
			try {
				//设置文件锁
				//fileLock.lock();
				String fileName=file.getName();
	            //获取文件的长度
	        	fileLength=file.length();
	        	//获取文件对应的线程数
	        	splitThreadNum=(int)getThreadNum(fileLength,filepageSize);
	        	//线程同步线程
	        	childrenLatch = new CountDownLatch(splitThreadNum);
	        	//
	        	fileSplitterMessageList=new ArrayList<FileSplitterMessage>(splitThreadNum);
	        	//文件分割的过程
	        	splitFile(filepath,filepageSize,fileName);
			} catch (Exception e) {
				e.printStackTrace();
			}finally{
				//释放文件锁
				//fileLock.unlock();
			}
		}
	}
	

	/**
	 * 总的线程数
	 * @param fileSize
	 * @param filepageSize
	 * @return
	 */
	public long getThreadNum(long fileSize,long filepageSize){
		long 	threadNum=(fileSize%filepageSize==0)?(fileSize%filepageSize):((fileSize/filepageSize)+1);
		return threadNum;
	}
	
	/**
	 * 分解文件
	 * @param filepath  文件路径
	 * @param filepageSize  文件的大小
	 */ 
	public void splitFile(String filepath,int filepageSize,String fileName){
				
		        //当前线程真正读取的长度
	        	int length=filepageSize;
	        	//创建一个线程池
	        	ExecutorService threadExePool = Executors.newFixedThreadPool(splitThreadNum);
	        	//循环创建线程
	        	for(int i=1;i<=splitThreadNum;i++){
	        		//起始点
	        		int startPos=(i-1)*filepageSize;
	        		
	        		int endPos=i*filepageSize;
	        		
	        		FileSplitterMessage  fileSplitterMessage=new FileSplitterMessage();
	        		
	        		String tempFileName=file.getName()+File_Split_Separator+splitThreadNum+File_Split_Separator+i+".tmp";
	        		
	        		File tempFile=getTempFileName(file.getParent(), tempFileName);
	        		
	        		//针对文件最后一个片度结束点和片段长度
	        		if((splitThreadNum==i)&&(fileLength%filepageSize!=0)){
	        			endPos=(int)fileLength;
	        			length=(int)fileLength-startPos;
	        		}
	        		//设置文件片段内容
        			fileSplitterMessage.setTempFile(tempFile);
        			//文件读取起始点
        			fileSplitterMessage.setStartPos(startPos);
        			//文件读取起始点
        			fileSplitterMessage.setEndPos(endPos);
        			//文件总长度
        			fileSplitterMessage.setFileLength(fileLength);
        			//文件路径
        			fileSplitterMessage.setFilePath(filepath);
        			//文件分割线程数
        			fileSplitterMessage.setSplitThreadNum(splitThreadNum);
        			//当前线程编号数
        			fileSplitterMessage.setCurrentThreadNum(i);
        			//读取的长度
        			fileSplitterMessage.setLen(length);
        			
        			System.out.println("fileSplitterMessage="+fileSplitterMessage);
        			//文件的名称
        			fileSplitterMessage.setFileName(file.getName());
        			fileSplitterMessageList.add(fileSplitterMessage);
	        }
	       try {
	    		if(logger.isDebugEnabled()){
					logger.debug("文件"+fileName+"开始分割....");
				}
	    		//线程池开始执行线程
	        	for (FileSplitterMessage fileMessage : fileSplitterMessageList) {
	        		threadExePool.execute(new FileSplitterFetch(fileMessage,masterLatch,childrenLatch));					
				}
				masterLatch.countDown();//宣布开始
				if(logger.isDebugEnabled()){
					logger.debug("文件"+fileName+"分割完毕....");
				}
				//等待CountdownLatch信号为0,表示所有子线程都结束。 
				childrenLatch.await();//等待结束
		 
				System.out.println("size ================="+fileSplitterMessageList.size());
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				System.out.println("整个文件"+fileName+"分割完毕");
				//开始执行发送文件
			}
			//注意线程已经要结束了,但是threadExePool线程如果不关闭是不会结束的
			threadExePool.shutdown();
	}


	public int getFilepageSize() {
		return filepageSize;
	}


	public void setFilepageSize(int filepageSize) {
		this.filepageSize = filepageSize;
	}


	public String getFilepath() {
		return filepath;
	}


	public void setFilepath(String filepath) {
		this.filepath = filepath;
	}


	public String getFileChannel() {
		return fileChannel;
	}

	public void setFileChannel(String fileChannel) {
		this.fileChannel = fileChannel;
	}

}

 

 

 

package easyway.tbs.file.transport.core.split;

import java.io.File;

import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;

/**
 * 文件分割一段的信息
 * 
 * 
 * @author longgangbai
 *
 */
public class FileSplitterMessage {
	
		/**
		 * 文件路径
		 */
		private String filePath;
		/**
		 * 文件总长度
		 */
		private long fileLength;
		
		/**
		 * 文件名称
		 */
		private String fileName;
		/**
		 * 分割线程数量
		 */
		private int splitThreadNum;
		
		/**
		 * 当前分割线程编号
		 */
		private int currentThreadNum;
		/**
		 * 文件开始读取的起始点
		 */
		private int startPos;
		/**
		 * 文件开始读取的结束点
		 */
		private int endPos;
		/**
		 * 文件起止点之间的数据信息
		 */
		private byte[] data;
		/**
		 * 临时文件
		 */
		private File tempFile;
		/**
		 * 临时文件的长度
		 */
		private long len;
		
		
		
		public byte[] getData() {
			return data;
		}
		public void setData(byte[] data) {
			this.data = data;
		}
		public int getEndPos() {
			return endPos;
		}
		public void setEndPos(int endPos) {
			this.endPos = endPos;
		}
		public long getFileLength() {
			return fileLength;
		}
		public void setFileLength(long fileLength) {
			this.fileLength = fileLength;
		}
		public String getFilePath() {
			return filePath;
		}
		public void setFilePath(String filePath) {
			this.filePath = filePath;
		}
		public int getSplitThreadNum() {
			return splitThreadNum;
		}
		public void setSplitThreadNum(int splitThreadNum) {
			this.splitThreadNum = splitThreadNum;
		}
		public int getStartPos() {
			return startPos;
		}
		public void setStartPos(int startPos) {
			this.startPos = startPos;
		}
		public int getCurrentThreadNum() {
			return currentThreadNum;
		}
		public void setCurrentThreadNum(int currentThreadNum) {
			this.currentThreadNum = currentThreadNum;
		}
		

		public String getFileName() {
			return fileName;
		}
		public void setFileName(String fileName) {
			this.fileName = fileName;
		}
		public File getTempFile() {
			return tempFile;
		}
		public void setTempFile(File tempFile) {
			this.tempFile = tempFile;
		}
	
	    @Override
		public String toString(){
			return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE);
		}
		public long getLen() {
			return len;
		}
		public void setLen(long len) {
			this.len = len;
		}
}

 

 

 

package easyway.tbs.file.transport.core.split;

import java.io.File;

import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;

/**
 * 文件分割一段的信息
 * 
 * 
 * @author longgangbai
 *
 */
public class FileSplitterMessage {
	
		/**
		 * 文件路径
		 */
		private String filePath;
		/**
		 * 文件总长度
		 */
		private long fileLength;
		
		/**
		 * 文件名称
		 */
		private String fileName;
		/**
		 * 分割线程数量
		 */
		private int splitThreadNum;
		
		/**
		 * 当前分割线程编号
		 */
		private int currentThreadNum;
		/**
		 * 文件开始读取的起始点
		 */
		private int startPos;
		/**
		 * 文件开始读取的结束点
		 */
		private int endPos;
		/**
		 * 文件起止点之间的数据信息
		 */
		private byte[] data;
		/**
		 * 临时文件
		 */
		private File tempFile;
		/**
		 * 临时文件的长度
		 */
		private long len;
		
		
		
		public byte[] getData() {
			return data;
		}
		public void setData(byte[] data) {
			this.data = data;
		}
		public int getEndPos() {
			return endPos;
		}
		public void setEndPos(int endPos) {
			this.endPos = endPos;
		}
		public long getFileLength() {
			return fileLength;
		}
		public void setFileLength(long fileLength) {
			this.fileLength = fileLength;
		}
		public String getFilePath() {
			return filePath;
		}
		public void setFilePath(String filePath) {
			this.filePath = filePath;
		}
		public int getSplitThreadNum() {
			return splitThreadNum;
		}
		public void setSplitThreadNum(int splitThreadNum) {
			this.splitThreadNum = splitThreadNum;
		}
		public int getStartPos() {
			return startPos;
		}
		public void setStartPos(int startPos) {
			this.startPos = startPos;
		}
		public int getCurrentThreadNum() {
			return currentThreadNum;
		}
		public void setCurrentThreadNum(int currentThreadNum) {
			this.currentThreadNum = currentThreadNum;
		}
		

		public String getFileName() {
			return fileName;
		}
		public void setFileName(String fileName) {
			this.fileName = fileName;
		}
		public File getTempFile() {
			return tempFile;
		}
		public void setTempFile(File tempFile) {
			this.tempFile = tempFile;
		}
	
	    @Override
		public String toString(){
			return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE);
		}
		public long getLen() {
			return len;
		}
		public void setLen(long len) {
			this.len = len;
		}
}

 

 

 

package easyway.tbs.file.transport.core.split;

/**
 * 分割文件测试类
 * @author longgangbai
 *
 */
public class FileSplitterManagerMain {
	
	public static void main(String[] args) {
		FileSplitterManager manager=new FileSplitterManager("E:/TestJoltProxy/RMAN.txt",10*1024*1024,"queue-svn");
		manager.splitFileMgr();
	}

}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值