有这样一个方法,使用MappedByteBuffer来拷贝文件
如下:        private static final int BUFFER_SIZE = 8 * 1024;
        /**
 * 将源文件拷贝一份到目的文件中
 * @param src    源文件
 * @param des  目的文件
 * @throws Exception  IO异常
 */
public static void fastCopyFile( File src, File des ) throws Exception {

RandomAccessFile raf1 = new RandomAccessFile(src, "rw");
RandomAccessFile raf2 = new RandomAccessFile(des, "rw");

FileChannel fcIn = raf1.getChannel();
FileChannel fcOut = raf2.getChannel();

long len = src.length();
long index = 1;
MappedByteBuffer mappedByteBuffer = null ;

while( true ){

/** 如果最后一次读写不足8K,则取文件长度 */
long l = index * BUFFER_SIZE;
if( l >= len ){
l = len ;
}

/** 映射文件部分内容到内存中 */
mappedByteBuffer = fcIn.map(FileChannel.MapMode.READ_WRITE, (index - 1) * BUFFER_SIZE, l);
fcIn.read(mappedByteBuffer);

/** 写入到目标文件中 */
mappedByteBuffer.flip();
fcOut.write(mappedByteBuffer);

/** 判断文件是否读写完毕 */
if( l == len ) { break; }

index ++;

/** 清空mappedByteBuffer */
mappedByteBuffer.clear();
//mappedByteBuffer = null;

}

/** 回收资源 */
mappedByteBuffer.clear();
fcOut.close();
fcIn.close();
raf2.close();
raf1.close();

}先说一下我的测试情况:
测试拷贝的文件为一步名为《后天》的电影,大小约594M。
电脑系统为XP,CPU为Pentium T4300,2.10GHZ。内存2G。
有几个问题想请教一下:
1.FileChannel.map这个方法应该是将此FileChannel连接的文件的一部分映射到MappedByteBuffer中,在上面的程序也就是映射8K的数据到mappedByteChannel中,但为什么程序一运行起来,消耗的内存一直疯长?一直超过594M,然后拷贝结束后,报了一个异常,我试过拷贝的文件,可以播放,里面的数据是没有问题的。异常情况如下:Exception in thread "main" java.io.IOException: Map failed
at sun.nio.ch.FileChannelImpl.map(FileChannelImpl.java:761)
at com.nio.example.FastCopyFile.fastCopyFile(FastCopyFile.java:68)
at com.nio.example.FastCopyFile.main(FastCopyFile.java:28)
Caused by: java.lang.OutOfMemoryError: Map failed
at sun.nio.ch.FileChannelImpl.map0(Native Method)
at sun.nio.ch.FileChannelImpl.map(FileChannelImpl.java:758)
... 2 more为什么会报异常?为什么内存一直疯长?
2.FileChannel.map这个方法调用之后mappedByteBuffer中间是不是已经有了文件中的数据了?为什么还需要fcIn.read(mappedByteBuffer);一次?
3.怎么关掉这个mappedByteBuffer?mappedByteBuffer = null它报异常。

解决方案 »

  1.   

    为什么不使用 FileChannel 的 transferTo 复制文件。
      

  2.   

    最近才看NIO,看到mappedByteBuffer这里有点不明白,谢谢你的指点,你能告诉我上面的问题为什么么?
      

  3.   

    transferTo 可以一次性的全部拷贝过去么?
    就像这样:fcIn.transferTo(0, src.length(), fcOut);
      

  4.   

    try {
        FileChannel srcChannel = new FileInputStream("srcfile").getChannel();
        FileChannel destChannel = new FileOutputStream("destfile").getChannel();    destChannel.transferFrom(srcChannel, 0, srcChannel.size());
        
        srcChannel.close();
        destChannel.close();
    } catch (IOException e){
        ...
    }
      

  5.   

    不用transfer也可以不用read,map之后直接用channel write出去也行的
      

  6.   

    我好像犯了一个低级的错误,file.length()返回的大小是按字节来计算的,也就是说594M的内容大小应该为
    len = 594 * 1024 * 1024 = 623265457字节。
    但程序中的l = 1990656时,拷贝的文件就已经达到了243M,应该是这样的  l = 243 * 1024 * 8 = 1990656
    ,这样就造成了条件 l > len几乎是不可能满足的,这是为什么呢?l的单位到底是什么呢?
      

  7.   

    内存上涨应该是你read引起的,你buffer虽然只有8K但是你使用的是read(buffer),而不是read(buffer,0,length)
    ,程序应该还是读取文件到文件尾然后再给buffer
    while循环里面没有什么需要关闭的,clear其实并没有将Buffer的内容清空,只是把position=0,limite=capacity
     = -1
    while循环出来再把相应的流和通道关闭就行了
      

  8.   

    - - 额,错了read没有这样三个参数的好吧 不是这个原因 等高手回答
      

  9.   

    你刚才已经把原因说出来了,我先也没注意到,你的l在最后一次相当于是500M了,而map最后个参数是分配的size
      

  10.   

    现在代码成这样了:public static void fastCopyFile( File src, File des ) throws Exception {

    RandomAccessFile raf1 = new RandomAccessFile(src, "r");
    RandomAccessFile raf2 = new RandomAccessFile(des, "rw");

    FileChannel fcIn = raf1.getChannel();
    FileChannel fcOut = raf2.getChannel();

    long len = src.length();
    long index = 1;
    MappedByteBuffer mappedByteBuffer = null ;
    boolean isFinished = false;

    while( true ){

    /** 如果最后一次读写不足8K,则取文件长度 */
    long begin = (index - 1) * BUFFER_SIZE;
    long length = BUFFER_SIZE;

    if( (begin + length)>= len ){
    isFinished = true;
    length = len - begin ;
    }

    /** 映射文件部分内容到内存中 */
    mappedByteBuffer = fcIn.map(FileChannel.MapMode.READ_ONLY, begin, length);

    /** 写入到目标文件中 */
    fcOut.write(mappedByteBuffer);


    /** 判断文件是否读写完毕 */
    if( isFinished ) { break; }

    index ++;

    /** 清空mappedByteBuffer */
    mappedByteBuffer.clear();

    }

    /** 回收资源 */
    mappedByteBuffer.clear();
    fcOut.close();
    fcIn.close();
    raf2.close();
    raf1.close();

    }
      

  11.   

    clear其实并没有做回收资源的作用,缓冲区还是那么大,原来的内容也都还在,他只是将position那些东西修改为初始值,使该buffer可以重新使用了,你看下源码会明白的