欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

使用FileChannel读取下载文件

程序员文章站 2023-12-30 14:49:40
...


前言

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显得更加有优势

参考链接

链接:
Java之FileChannel类的理解和使用.姚镜堂
ava之FileChannel类的理解和使用 -----java 流NIO的使用.fight_man001

相关标签: nio

上一篇:

下一篇: