Java-复制文件使用fileChannel 复制

73 篇文章 2 订阅
36 篇文章 0 订阅

复制文件,注意,使用fileChannel 复制,不能使用Files.copy

Java NIO FileChannel是连接文件的通道,从文件中读取数据和将数据写入文件。Java NIO FileChannel类是NIO用于替代使用标准Java IO API读取文件的方法。

FileInputStream的getChannel方法获取的文件通道是只读的,当然通过FileOutputStream的getChannel的方法获取的文件通道是可写的部分API

————————————————————————————————————————

第一种:
参考:Java NIO FileChannel读写复制文件
常规的文件复制,包含对源文件的读,以及把读出来的文件写入到目标复制文件。
如下:

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
 
public class FileTest {
	public static void main(String[] args) {
		try {
			new FileTest();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
 
	public FileTest() throws Exception {
		String pathIn = "D:/code/file.txt";
		String pathOut = "D:/code/copy.txt";
 
		FileChannel fcIn = new RandomAccessFile(pathIn, "rw").getChannel();
		System.out.println("源文件总长度:" + fcIn.size());
 
		System.out.println("源文件全部内容:");
		printer(pathIn);
 
		FileChannel fcOut = new FileOutputStream(pathOut).getChannel();
 
		final int capacity = 4;
		ByteBuffer buffer = ByteBuffer.allocate(capacity);
 
		int count = 0;
		while (true) {
			count = fcIn.read(buffer);
			if (count == -1) {
				break;
			}
 
			buffer.flip();
			fcOut.write(buffer);
			buffer.clear();
		}
 
		System.out.println("复制文件总长度:" + fcOut.size());
		System.out.println("复制文件全部内容:");
		printer(pathOut);
	}
 
	// 打印path指向的文件中的内容。
	private void printer(String path) throws Exception {
		InputStream is = new FileInputStream(path);
		BufferedReader br = new BufferedReader(new InputStreamReader(is));
		String s = null;
		while (true) {
			s = br.readLine();
			if (s != null)
				System.out.println(s);
			else
				break;
		}
 
		br.close();
		is.close();
	}
}

运行后输出:
在这里插入图片描述
在Java NIO中,用FileChannel复制文件有更简单的方案,一行代码实现,如下:

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
 
public class FileTest {
	public static void main(String[] args) {
		try {
			new FileTest();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
 
	public FileTest() throws Exception {
		String pathIn = "D:/code/file.txt";
		String pathOut = "D:/code/copy.txt";
 
		FileChannel fcIn = new RandomAccessFile(pathIn, "rw").getChannel();
		System.out.println("源文件总长度:" + fcIn.size());
 
		System.out.println("源文件全部内容:");
		printer(pathIn);
 
		FileChannel fcOut = new FileOutputStream(pathOut).getChannel();
 
		// FileChannel方式的复制,一行代码实现。
		fcIn.transferTo(0, fcIn.size(), fcOut);
 
		System.out.println("复制文件总长度:" + fcOut.size());
		System.out.println("复制文件全部内容:");
		printer(pathOut);
	}
 
	// 打印path指向的文件中的内容。
	private void printer(String path) throws Exception {
		InputStream is = new FileInputStream(path);
		BufferedReader br = new BufferedReader(new InputStreamReader(is));
		String s = null;
		while (true) {
			s = br.readLine();
			if (s != null)
				System.out.println(s);
			else
				break;
		}
 
		br.close();
		is.close();
	}
}

————————————————————————————————————————

第二种:

FileChannel在使用前,必须要打开。需要通过InputStream/OutputStream/RandomAccessFile获取,BufferedReader/BufferedWriter获取不到。

 * @see java.io.FileInputStream#getChannel()
 * @see java.io.FileOutputStream#getChannel()
 * @see java.io.RandomAccessFile#getChannel()

使用FileChannel进行文件复制:

public static void fileChannelCopy(File src,File dst) throws IOException {
		FileChannel inChannel =new FileInputStream(src).getChannel();
		FileChannel outChannel=new FileOutputStream(dst).getChannel();
		
		inChannel.transferTo(0, inChannel.size(), outChannel);
		
		inChannel.close();
		outChannel.close();
	}

使用普通BufferedInputStream进行文件复制:

public static void bufferedCopy(File src,File dst) throws IOException {
		BufferedInputStream bis=new BufferedInputStream(new FileInputStream(src));
		BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream(dst));
		int line=0;
		byte[] buf=new byte[1024*10];
		while((line=bis.read(buf))!=-1) {
			bos.write(buf);
		}
		bis.close();
		bos.close();
	}

测试:

public static void main(String[] args) throws IOException {
		File src = new File("E:\\myCode\\kaifa.txt");
		File dstFileChannel = new File("E:\\myCode\\kaifa1.txt");
		File dstBufferd = new File("E:\\myCode\\kaifa2.txt");
 
		long start = System.currentTimeMillis();
		fileChannelCopy(src, dstFileChannel);
		long end = System.currentTimeMillis();
		System.out.println("fileChannelCopy: " + (end - start) + " ms");
 
		start = System.currentTimeMillis();
		bufferedCopy(src, dstBufferd);
		end = System.currentTimeMillis();
		System.out.println("bufferedCopy " + (end - start) + " ms");
	}

对比:
在这里插入图片描述
————————————————————————————————————————
第三种:
参考:java之通过FileChannel实现文件复制

truncate截断文件功能:

   /**
     * Truncates the file underlying this channel to a given size. Any bytes
     * beyond the given size are removed from the file. If there are no bytes
     * beyond the given size then the file contents are unmodified.
     * <p>
     * If the file position is currently greater than the given size, then it is
     * set to the new size.
     *
     * @param size
     *            the maximum size of the underlying file.
     * @throws IllegalArgumentException
     *             if the requested size is negative.
     * @throws ClosedChannelException
     *             if this channel is closed.
     * @throws NonWritableChannelException
     *             if the channel cannot be written to.
     * @throws IOException
     *             if another I/O error occurs.
     * @return this channel.
     */
    public abstract FileChannel truncate(long size) throws IOException;

force()强制在内存中的数据刷新到硬盘中去:

    /**
     * Requests that all updates to this channel are committed to the storage
     * device.
     * <p>
     * When this method returns, all modifications made to the platform file
     * underlying this channel have been committed if the file resides on a
     * local storage device. If the file is not hosted locally, for example on a
     * networked file system, then applications cannot be certain that the
     * modifications have been committed.
     * <p>
     * There are no assurances given that changes made to the file using methods
     * defined elsewhere will be committed. For example, changes made via a
     * mapped byte buffer may not be committed.
     * <p>
     * The <code>metadata</code> parameter indicates whether the update should
     * include the file's metadata such as last modification time, last access
     * time, etc. Note that passing <code>true</code> may invoke an underlying
     * write to the operating system (if the platform is maintaining metadata
     * such as last access time), even if the channel is opened read-only.
     *
     * @param metadata
     *            {@code true} if the file metadata should be flushed in
     *            addition to the file content, {@code false} otherwise.
     * @throws ClosedChannelException
     *             if this channel is already closed.
     * @throws IOException
     *             if another I/O error occurs.
     */
    public abstract void force(boolean metadata) throws IOException;

transferFrom可以看出是拷贝从源的position位置的count 字节大小:

    /**
     * Reads up to {@code count} bytes from {@code src} and stores them in this
     * channel's file starting at {@code position}. No bytes are transferred if
     * {@code position} is larger than the size of this channel's file. Less
     * than {@code count} bytes are transferred if there are less bytes
     * remaining in the source channel or if the source channel is non-blocking
     * and has less than {@code count} bytes immediately available in its output
     * buffer.
     * <p>
     * Note that this channel's position is not modified.
     *
     * @param src
     *            the source channel to read bytes from.
     * @param position
     *            the non-negative start position.
     * @param count
     *            the non-negative number of bytes to transfer.
     * @return the number of bytes that are transferred.
     * @throws IllegalArgumentException
     *             if the parameters are invalid.
     * @throws NonReadableChannelException
     *             if the source channel is not readable.
     * @throws NonWritableChannelException
     *             if this channel is not writable.
     * @throws ClosedChannelException
     *             if either channel has already been closed.
     * @throws AsynchronousCloseException
     *             if either channel is closed by other threads during this
     *             operation.
     * @throws ClosedByInterruptException
     *             if the thread is interrupted during this operation.
     * @throws IOException
     *             if any I/O error occurs.
     */
    public abstract long transferFrom(ReadableByteChannel src, long position,
            long count) throws IOException;

复制文件常用方法
1、通过普通输入输出流复制文件

    public void copyFile(File srcFile, File dstFile) throws FileNotFoundException {
        InputStream inputStream = null;
        OutputStream outputStream = null;
        try {
            inputStream = new BufferedInputStream(new FileInputStream(srcFile));
            outputStream = new BufferedOutputStream(new FileOutputStream(dstFile));
            byte[] bytes = new byte[1024];
            int i;
            //读取到输入流数据,然后写入到输出流中去,实现复制
            while ((i = inputStream.read(bytes)) != -1) {
                outputStream.write(bytes, 0, i);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (inputStream != null)
                    inputStream.close();
                if (outputStream != null)
                    outputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

2、通过 FileChannel复制文件

	public void copyFile(File srcFile, File dstFile) throws IOException {
		if (srcFile == null || !srcFile.exists()) {
			return;
		}
		if (dstFile == null || !dstFile.exists()) {
			return;
		}
 
		FileInputStream fileIns = null;
		FileOutputStream fileOuts = null;
		FileChannel source = null;
		FileChannel destination = null;
 
		try {
			fileIns = new FileInputStream(srcFile);
			fileOuts = new FileOutputStream(dstFile);
			source = fileIns.getChannel();
			destination = fileOuts.getChannel();
			destination.transferFrom(source, 0, source.size());
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (fileIns != null)
				fileIns.close();
			if (fileOuts != null)
				fileOuts.close();
			if (source != null)
				source.close();
			if (destination != null)
				destination.close();
		}
	}

总结
一般复制使用输入输出流进行操作,用源文件创建出一个输入流,用目标文件创建出一个输出流,把输入流的数据读取写入到输出流,用fileChannel,直接连接输入输出流的文件通道,将数据直接写入到目标文件中,效率很高,尤其是复制文件比较大的时候,我们一般采用fileChannel复制文件。
————————————————
FileChannel是线程安全的,这就在很多场合我们可以很好的利用这一性能

* <p> File channels are safe for use by multiple concurrent threads.  The
 * {@link Channel#close close} method may be invoked at any time, as specified
 * by the {@link Channel} interface.  Only one operation that involves the
 * channel's position or can change its file's size may be in progress at any
 * given time; attempts to initiate a second such operation while the first is
 * still in progress will block until the first operation completes.  Other
 * operations, in particular those that take an explicit position, may proceed
 * concurrently; whether they in fact do so is dependent upon the underlying
 * implementation and is therefore unspecified.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值