复制文件,注意,使用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.