使用FileChannel读取下载文件
使用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显得更加有优势
参考链接
链接:
Java之FileChannel类的理解和使用.姚镜堂
ava之FileChannel类的理解和使用 -----java 流NIO的使用.fight_man001