使用FileChannel读取下载文件

  • 前言
  • 一、FileChannel是什么?
  • 二、FileChannal的方法介绍
  • 1.open
  • 2.read
  • 3.write
  • 4.lock()与tryLock()
  • 三、FileChannel下载网络图片
  • 总结
  • 参考链接



前言

FileChannal的理解和使用场景
对于文件的复制,平时我们都是使用输入输出流进行操作,利用源文件创建出一个输入流,然后利用目标文件创建出一个输出流,最后将输入流的数据读取写入到输出流中。这样也是可以进行操作的。但是利用fileChannel是很有用的一个方式。它能直接连接输入输出流的文件通道,将数据直接写入到目标文件中去。而且效率更高。
1.多线程情况下读取文件流
2.特定的大文件的读取等操作


一、FileChannel是什么?

FileChannel是通过创建channal 通道,操作一个文件。除了有读写操作之外,还有裁剪特定大小文件truncate(),强制在内存中的数据刷新到硬盘中去force(),对通道上锁lock()等功能

二、FileChannal的方法介绍

1.open

创建一个文件,并且返回一个channal,允许我们进入文件,对文件进行读写操作。

FileChannel.open(new File("D:\\hello.txt").toPath(), StandardOpenOption.CREATE_NEW);

StandardOpenOption 包含多种文件操作属性,比如:
READ(读),WRITE(写),SYNC/DSYNC (同步或异步IO)
当然,我们还可以通过FileOutputStream 文件流直接获取channal

File file=new File("D:\\hello.txt");
FileOutputStream fileOutputStream = new FileOutputStream(file);
FileChannel channel = fileOutputStream.getChannel();

2.read

代码如下(示例):

ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
//读取1024字节内容到byteBuffer钟
fileChannel.read(byteBuffer);

3.write

代码如下(示例):

//写数据到channel
fileChannelOutput.write(byteBuffer);

4.lock()与tryLock()

lock()与tryLock()方法都是尝试去获取在某一文件上的独有锁(以下简称独有锁),可以实现进程间操作的互斥。区别在于lock()会阻塞(blocking)方法的执行,tryLock()则不会。
测试代码:

private void processLockTest() {
    try {
        File file = new File(getFilesDir().getAbsolutePath() + File.separator + "lock.lock");
        FileOutputStream fos = new FileOutputStream(file);
        FileLock fl = null;
        boolean loop = true;
        int time = 0;
        LogUtil.log(android.os.Process.myPid() + "   " + "while start" + "  " + file.getAbsolutePath());
        while (loop) {
            time++;
            LogUtil.log(android.os.Process.myPid() + "   " + "loop 次数:" + time);
            if (fl == null) {
                LogUtil.log(android.os.Process.myPid() + "   " + "tryLock before" + " " + file.getAbsolutePath());
                try {
                    fl = fos.getChannel().tryLock();
                } catch (Exception e) {
                    e.printStackTrace();
                    LogUtil.log(android.os.Process.myPid() + "   " + "tryLock IOException" + " " + e.toString());
                }
                LogUtil.log(android.os.Process.myPid() + "   " + "tryLock after" + " " + file.getAbsolutePath());
            }
            if (fl != null) {
                while (loop) {
                    Thread.sleep(2000);
                    time++;
                    if (time == 5) {
                        fl.release();
                    }
                    if (time == 10) {
                        loop = false;
                    }
                    if (fl != null) {
                        LogUtil.log(System.currentTimeMillis() + "  " + android.os.Process.myPid() + "  " + "got");
                    } else {
                        LogUtil.log(System.currentTimeMillis() + "  " + android.os.Process.myPid() + "  " + "fl is null");
                    }
                }
            } else {
                LogUtil.log(System.currentTimeMillis() + "  " + android.os.Process.myPid() + "  " + "ungot");
            }
            Thread.sleep(2000);
        }
        LogUtil.log(android.os.Process.myPid() + "   " + "while over");
    } catch (Throwable t) {
 
    }
}

三、FileChannel下载网络图片

该处使用的url网络请求的数据。

/**
     * 
     * @param uri 网络图片地址
     * @param filePath 下载目录
     * @param fileName 下载文件名
     */
    public static void downLoadImage(String uri, String filePath,String fileName) {
        ReadableByteChannel readableByteChannel = null;
        FileChannel fileChannel = null;
        File file;
        URL url;
        FileOutputStream fileOutputStream = null;
        try {
            url = new URL(uri);
            //首先从 URL stream 中创建一个 ReadableByteChannel 来读取网络文件
            readableByteChannel = Channels.newChannel(url.openStream());
            String path = filePath + fileName;
            file = new File(path);
            if (!file.getParentFile().exists() && !file.getParentFile().isDirectory()) {
                file.getParentFile().mkdirs();
            }
            //通过 ReadableByteChannel 读取到的字节会流动到一个 FileChannel 中,然后再关联一个本地文件进行下载操作
            fileOutputStream = new FileOutputStream(file);
            fileChannel = fileOutputStream.getChannel();
            //最后用 transferFrom()方法就可以把 ReadableByteChannel 获取到的字节写入本地文件
            fileChannel.transferFrom(readableByteChannel, 0, Long.MAX_VALUE);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (null != readableByteChannel) {
                    readableByteChannel.close();
                }
                if (null != fileChannel) {
                    fileChannel.close();
                }
                if (null != fileOutputStream) {
                    fileOutputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }

测试下载图片

public static void main(String[] args) throws IOException {
        String fileUrl = "https://img.pconline.com.cn/images/upload/upc/tx/itbbs/1308/16/c8/24543026_1376637095791_mthumb.jpg";
        downLoadImage(fileUrl,"D:/test/","hello.jpg");
    }

总结

利用filechannel使用的时间比普通的读取输入时间缩短了将近一半。尤其是在进行大文件复制的时候,filechannel显得更加有优势