`
sunxboy
  • 浏览: 2876820 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
社区版块
存档分类
最新评论

Apache commons IO简介

阅读更多

 

【一】Apache commons IO

首先贴一段Apache commons IO官网上的介绍,来对这个著名的开源包有一个基本的了解:

Commons IO is a library of utilities to assist with developing IO functionality. There are four main areas included:

 ●Utility classes - with static methods to perform common tasks 
 ●Filters - various implementations of file filters 
 ●Comparators - various implementations of java.util.Comparator for files
 ●Streams - useful stream, reader and writer implementations

Packages

org.apache.commons.io

This package defines utility classes for working with streams, readers, writers and files.

org.apache.commons.io.comparator

This package provides various Comparator implementations for Files.

org.apache.commons.io.filefilter

This package defines an interface (IOFileFilter) that combines both FileFilter and FilenameFilter.

org.apache.commons.io.input

This package provides implementations of input classes, such as InputStream and Reader.

org.apache.commons.io.output

This


【二】org.apache.comons.io.input包介绍

这个包针对SUN JDK IO包进行了扩展,实现了一些功能简单的IO类,主要包括了对字节/字符输入流接口的实现
<!--[if !supportLineBreakNewLine]-->

 

 

 

 

这个包针对java.io.InputStreamReader进行了扩展,其中比较实用的有以下几个:

●AutoCloseInputStream

Proxy stream that closes and discards the underlying stream as soon as the end of input has been reached or when the stream is explicitly closed. Not even a reference to the underlying stream is kept after it has been closed, so any allocated in-memory buffers can be freed even if the client application still keeps a reference to the proxy stream

This class is typically used to release any resources related to an open stream as soon as possible even if the client application (by not explicitly closing the stream when no longer needed) or the underlying stream (by not releasing resources once the last byte has been read) do not do that.

这个输入流是一个底层输入流的代理,它能够在数据源的内容被完全读取到输入流后,后者当用户调用close()方法时,立即关闭底层的输入流。释放底层的资源(例如文件的句柄)。这个类的好处就是避免我们在代码中忘记关闭底层的输入流而造成文件处于一直打开的状态。

我们知道对于某些文件,只允许由一个进程打开。如果我们使用后忘记关闭那么该文件将处于一直打开的状态,其它进程无法读写。例如下面的例子:

 new BufferedInputStream(new FileInputStream(FILE))

里面的FileInputStream(FILE)在打开后不能被显式关闭,这将导致可能出现的问题。如果我们使用了AutoCloseInputStream,那么当数据读取完毕后,底层的输入流会被自动关闭,迅速地释放资源。

 new BufferedInpu
tStream(new AutoClosedInputStream(new FileInputStream));

那么这个类是如何做到自动关闭的呢?来看看这个非常简单的类的代码吧

 

 

package org.apache.commons.io.input;
 
 import java.io.IOException;
 import java.io.InputStream;
 
 
  public class AutoCloseInputStream extends ProxyInputStream {
 
     public AutoCloseInputStream(InputStream in) {
         super(in);
     }
 
      public void close() throws IOException {
         in.close();
         in = new ClosedInputStream();
     }
 
    public int read() throws IOException {
         int n = in.read();
          if (n == -1) {
             close();
         }
         return n;
     }
 
      public int read(byte[] b) throws IOException {
         int n = in.read(b);
          if (n == -1) {
             close();
         }
         return n;
     }
 
  
      public int read(byte[] b, int off, int len) throws IOException {
         int n = in.read(b, off, len);
          if (n == -1) {
             close();
         }
         return n;
     }
 
      protected void finalize() throws Throwable {
         close();
         super.finalize();
     }
 
 }
 
  public class ClosedInputStream extends InputStream {
     
      /**
      * A singleton.
      */
     public static final ClosedInputStream CLOSED_INPUT_STREAM = new ClosedInputStream();
 
      /**
      * Returns -1 to indicate that the stream is closed.
      *
      * @return always -1
      */
      public int read() {
         return -1;
     }
 
 }

 

 

可以看到这个类通过两个途径来保证底层的流能够被正确地关闭:
 
①每次调用read方法时,如果底层读到的是-1,立即关闭底层输入流。返回一个ClosedInputStream
 
②当这个类的对象被回收时,确保关闭底层的输入流

●TeeInputStream

InputStream proxy that transparently writes a copy of all bytes read from the proxied stream to a given OutputStream. The proxied input stream is closed when the close() method is called on this proxy. It is configurable whether the associated output stream will also closed.

可以看到这个类的作用是把输入流读入的数据原封不动地传递给输出流。这一点和JDK中提供的PipedInputStream的理念有些类似。在实际使用 中可以非常方便地做到像:将从远程URL读入的数据写到输出流,保存到文件之类的动作。当输入流被关闭时,输出流不一定被关闭。可以依然保持打开的状态。

下面是这个类的部分源码

public int read(byte[] bts, int st, int end) throws IOException {
         int n = super.read(bts, st, end);
          if (n != -1) {
             branch.write(bts, st, n);
         }
         return n;
     }

 

 


●CharSequenceReader

Reader implementation that can read from String, StringBuffer, StringBuilder or CharBuffer.

这个类可以看成是对StringReader的一个扩展,用于从内存中读取字符。

●NullReader

A functional, light weight Reader that emulates a reader of a specified size.

This implementation provides a light weight object for testing with an Reader where the contents don't matter.

One use case would be for testing the handling of large Reader as it can emulate that scenario without the overhead of actually processing large numbers of characters - significantly speeding up test execution times.

从上面的文字描述来看,这个类显然是用来做测试辅助的,它的目标对象是对读入内容不关心的需求。它并不传递真正的数据,而是模拟这个过程。来看看下面的源代码

      /**
      * Read the specified number characters into an array.
      *
      * @param chars The character array to read into.
      * @param offset The offset to start reading characters into.
      * @param length The number of characters to read.
      * @return The number of characters read or <code>-1</code>
      * if the end of file has been reached and
      * <code>throwEofException</code> is set to <code>false</code>.
      * @throws EOFException if the end of file is reached and
      * <code>throwEofException</code> is set to <code>true</code>.
      * @throws IOException if trying to read past the end of file.
      */
      public int read(char[] chars, int offset, int length) throws IOException {
        if (eof) {
             throw new IOException("Read after end of file");
         }
        if (position == size) {
             return doEndOfFile();
         }
         position += length;
         int returnLength = length;
        if (position > size) {
             returnLength = length - (int)(position - size);
             position = size;
         }
         processChars(chars, offset, returnLength);
         return returnLength;
     }
 
     /**
      * Return a character value for the  <code>read()</code> method.
      * <p>
      * This implementation returns zero.
      *
      * @return This implementation always returns zero.
      */
      protected int processChar() {
         // do nothing - overridable by subclass
         return 0;
     }
 
      /**
      * Process the characters for the <code>read(char[], offset, length)</code>
      * method.
      * <p>
      * This implementation leaves the character array unchanged.
      *
      * @param chars The character array
      * @param offset The offset to start at.
      * @param length The number of characters.
      */
      protected void processChars(char[] chars, int offset, int length) {
         // do nothing - overridable by subclass
     }

 

知道它是怎么模拟的了吗?呵呵~~。原来它只是模拟计数的过程,根本不传递、处理、存储任何数据。数组始终都是空的。

【三】org.apache.commons.io.output包介绍
<!--[if !supportLineBreakNewLine]-->

input包类似,output包也实现/继承了部分JDK IO包的类、接口。这里需要特别注意的有3个类,他们分别是:

 ByteArrayOutputStream
 
FileWriterWithEncoding
 
LockableFileWriter

●ByteArrayOutputStream

This class implements an output stream in which the data is written into a byte array. The buffer automatically grows as data is written to it.

The data can be retrieved using toByteArray() and toString().

Closing a ByteArrayOutputStream has no effect. The methods in this class can be called after the stream has been closed without generating an IOException.

This is an alternative implementation of the java.io.ByteArrayOutputStream class. The original implementation only allocates 32 bytes at the beginning. As this class is designed for heavy duty it starts at 1024 bytes. In contrast to the original it doesn't reallocate the whole memory block but allocates additional buffers. This way no buffers need to be garbage collected and the contents don't have to be copied to the new buffer. This class is designed to behave exactly like the original. The only exception is the deprecated toString(int) method that has been ignored.

从上面的文档中,我们看到Apache commons ioByteArrayOutputString比起SUN自带的ByteArrayOutputStream更加高效,原因在于:

 
①缓冲区的初始化大小比原始的JDK自带的ByteArrayOutputStream要大很多(1024:32)
 
②缓冲区的大小可以无限增加。当缓冲不够时动态增加分配,而非清空后再重新封闭
 
③减少write方法的调用次数,一次性将多个一级缓冲数据写出。减少堆栈调用的时间

那么为什么这个类可以做到这些呢?来看看他的源码吧:

 

      /** The list of buffers, which grows and never reduces. */
     private List buffers = new ArrayList();    
      /** The current buffer. */
     private byte[] currentBuffer;

  /**
      * Creates a new byte array output stream. The buffer capacity is 
      * initially 1024 bytes, though its size increases if necessary. 
      */
      public ByteArrayOutputStream() {
         this(1024);
     }

 

 

JDK自带的Buffer则只有简单的一个byte[]

        /** 
      * The buffer where data is stored. 
      */
     protected byte buf[];

   /**
      * Creates a new byte array output stream. The buffer capacity is 
      * initially 32 bytes, though its size increases if necessary. 
      */
      public ByteArrayOutputStream() {
     this(32);
     }

 

 

原来Apache commons io是采用了二级缓冲:首先一级缓冲是一个byte[],随着每次写出的数据不同而不同。二级缓冲则是一个无限扩充的ArrayList,每次从 byte[]中要写出的数据都会缓存到这里。当然效率上要高很多了。那么这个类是如何做到动态增加缓冲而不需要每次都回收已有的缓冲呢?

  /**
      * Makes a new buffer available either by allocating
      * a new one or re-cycling an existing one.
      *
      * @param newcount  the size of the buffer if one is created
      */
      private void needNewBuffer(int newcount) {
          if (currentBufferIndex < buffers.size() - 1) {
             //Recycling old buffer
             filledBufferSum += currentBuffer.length;
             
             currentBufferIndex++;
             currentBuffer = getBuffer(currentBufferIndex);
          } else {
             //Creating new buffer
             int newBufferSize;
              if (currentBuffer == null) {
                 newBufferSize = newcount;
                 filledBufferSum = 0;
              } else {
                 newBufferSize = Math.max(
                     currentBuffer.length << 1, 
                     newcount - filledBufferSum);
                 filledBufferSum += currentBuffer.length;
             }
             
             currentBufferIndex++;
             currentBuffer = new byte[newBufferSize];
             buffers.add(currentBuffer);
         }
     }

 

 

在初始化的情况下,currentBuffer == null,于是第一个一级缓冲区byte[]的大小就是默认的1024或者用户指定的值。然后filledBufferSum currentBufferIndex分别进行初始化。创建第一个一级缓存区,添加到二级缓冲区buffers中。

当后续的缓冲请求到来后,根据剩下的缓冲大小和尚存的缓冲进行比较,然后选择较大的值作为缓冲扩展的大小。再次创建一个新的一级缓冲byte[],添加到二级缓冲中。

相比于JDK自带的方法,这个类多了一个write(InputStream in)的方法,看看下面的源代码

public synchronized int write(InputStream in) throws IOException {
         int readCount = 0;
         int inBufferPos = count - filledBufferSum;
         int n = in.read(currentBuffer, inBufferPos, currentBuffer.length - inBufferPos);
          while (n != -1) {
             readCount += n;
             inBufferPos += n;
             count += n;
              if (inBufferPos == currentBuffer.length) {
                 needNewBuffer(currentBuffer.length);
                 inBufferPos = 0;
             }
             n = in.read(currentBuffer, inBufferPos, currentBuffer.length - inBufferPos);
         }
         return readCount;
     }

 

 

可以看到每次从InputStream读取当前一级缓冲剩余空间大小的字节,缓冲到剩下的空间。如果缓冲满了则继续分配新的一级缓冲。直至数据读完。对于写出到另外的输出流,则是:

public synchronized void writeTo(OutputStream out) throws IOException {
         int remaining = count;
          for (int i = 0; i < buffers.size(); i++) {
             byte[] buf = getBuffer(i);
             int c = Math.min(buf.length, remaining);
             out.write(buf, 0, c);
             remaining -= c;
              if (remaining == 0) {
                 break;
             }
         }
     }

 

 

由于每次write的时候一次性地写出一级缓冲,而且是将二级缓冲全部写出,减少了调用的次数,所以提高了效率。

FileWriterWithEncoding

从这个类的名称已经可以很清楚的知道它的作用了。在JDK自带的FileWriter中,是无法设置encoding的,这个类允许我们采用默认或者指定的encoding,以字符的形式写到文件。为什么这个类可以改变字符嗯?

原理很简单:无非使用了OutputStreamWriter。而且这个类并不是继承与FileWriter,而是直接继承于Writer

OutputStream stream = null;
         Writer writer = null;
          try {
             stream = new FileOutputStream(file, append);
              if (encoding instanceof Charset) {
                 writer = new OutputStreamWriter(stream, (Charset)encoding);
              } else if (encoding instanceof CharsetEncoder) {
                 writer = new OutputStreamWriter(stream, (CharsetEncoder)encoding);
              } else {
                 writer = new OutputStreamWriter(stream, (String)encoding);
             }
    

 

 

剩下的各种write方法,无非就是decorator模式而已。

LockableFileWriter

使用文件锁而非对象锁来限制多线程环境下的写动作。这个类采用在JDK默认的系统临时目录下写文件:java.io.tmpdir属性。而且允许我们设置encoding

/**
      * Constructs a LockableFileWriter with a file encoding.
      *
      * @param file  the file to write to, not null
      * @param encoding  the encoding to use, null means platform default
      * @param append  true if content should be appended, false to overwrite
      * @param lockDir  the directory in which the lock file should be held
      * @throws NullPointerException if the file is null
      * @throws IOException in case of an I/O error
      */
     public LockableFileWriter(File file, String encoding, boolean append,
              String lockDir) throws IOException {
         super();
         // init file to create/append
         file = file.getAbsoluteFile();
        if (file.getParentFile() != null) {
             FileUtils.forceMkdir(file.getParentFile());
         }
        if (file.isDirectory()) {
             throw new IOException("File specified is a directory");
         }
         
         // init lock file
        if (lockDir == null) {
             lockDir = System.getProperty("java.io.tmpdir");
         }
         File lockDirFile = new File(lockDir);
         FileUtils.forceMkdir(lockDirFile);
         testLockDir(lockDirFile);
         lockFile = new File(lockDirFile, file.getName() + LCK);
         
         // check if locked
         createLock();
         
         // init wrapped writer
         out = initWriter(file, encoding, append);
     }

 

 

首先创建一个用于存放lock文件的目录,位于系统临时目录下。

接下来创建一个位于该目录下的名为xxxLCK的文件(锁文件)

然后创建锁

最后则是初始化writer

显然我们关心的是如何创建这个锁,以及如何在写期间进行锁。首先来看创建锁的过程

private void createLock() throws IOException {
          synchronized (LockableFileWriter.class) {
              if (!lockFile.createNewFile()) {
                 throw new IOException("Can't write file, lock " +
                         lockFile.getAbsolutePath() + " exists");
             }
             lockFile.deleteOnExit();
         }
     }

 

 

注意这里的deleteOnExit()方法很重要,它告诉JVM:当JV退出是要删除该文件。否则磁盘上将有无数无用的临时锁文件。

下面的问题则是如何实现锁呢?呵呵~~。还是回到这个上面这个类的构造方法吧,我们看到在构造这个LockableFileWriter时,会调用 createLock()这个方法,而这个方法如果发现文件已经创建/被其它流引用时,会抛出一个IOException。于是创建不成功,也就无法继续 后续的write操作了。

那么如果第一个进程创建了锁之后就不释放,那么后续的进程岂不是无法写了,于是在这个类的close方法中有这样一句代码:

public void close() throws IOException {
          try {
             out.close();
          } finally {
             lockFile.delete();
         }
     }

 

每个进程在完成数据的写动作后,必须调用close()方法,于是锁文件被删除,锁被解除。相比于JDK中自带Writer使用的object (synchronized(object)),这个方法确实要更加简便和高效。这个类当初就是被设计来替换掉原始的FileWriter的。


但切记:一定要在结尾处调用close()方法,否则无法解锁。而且这里没有AutoCloseOutputStream这样的类哦!


分享到:
评论

相关推荐

    Apache commons-io-2.5.jar

    Apache Commons IO 是一个Java库,专门用于处理输入/输出(I/O)操作。这个库提供了大量的实用工具类,使得在处理文件、流、过滤器、读写操作时更加方便。"commons-io-2.5.jar"是Apache Commons IO库的一个版本,...

    apache commons IO工具包

    ### Apache Commons IO工具包知识点详解 #### 一、Apache Commons IO概述 Apache Commons IO是Apache Commons项目中的一个子项目,它提供了一系列与IO操作相关的工具类。这些工具类旨在简化和增强Java平台上的文件...

    org.apache.commons.io 的jar包大全

    Apache Commons IO 是一个Java库,它提供了一系列与输入/输出流、文件操作、I/O utility类等相关的实用工具。这个库极大地简化了处理输入和输出流的复杂性,并且提供了许多方便的功能,使得在Java项目中进行IO操作...

    commons-fileupload和commons-io的jar包

    Apache Commons FileUpload与Apache Commons IO是Java开发中两个非常重要的库,它们主要用于处理文件上传和I/O操作。这两个库在Web应用、数据处理以及文件管理等领域广泛应用。 Apache Commons FileUpload库是一个...

    apache commons IO 工具包使用

    ### Apache Commons IO 工具包使用 #### 一、Apache Commons IO 概述 Apache Commons IO 是 Apache Commons 项目下的一个子项目,主要用于提供一系列针对 Java IO 操作的实用工具类。这些工具类大大简化了文件和流...

    org.apache.commons.io 包

    Apache Commons IO 是一个Java开发库,它提供了许多实用的工具类来处理输入/输出操作。这个包中的类弥补了Java标准库在IO操作上的不足,为开发者提供了更强大的功能和便利性。`org.apache.commons.io`是这个库的核心...

    commons-io-2.11.0-bin.zip

    Apache Commons IO是一个非常重要的Java库,它提供了大量的实用工具类,用于处理输入/输出操作。在标题中提到的"commons-io-2.11.0-bin.zip"是Apache Commons IO库的一个二进制发行版,版本号为2.11.0。这个压缩包...

    commons-io所有版本(0.1-2.4).zip

    Apache Commons IO 是一个Java库,它提供了一系列实用工具类来处理输入/输出操作。这个压缩包包含从0.1版本到2.4版本的所有Apache Commons IO的发布。这些版本跨越了多个年份,反映了该库在发展过程中的功能增强、...

    2018年org.apache.commons.io jar包

    项目中需要 org.apache.commons.io.IOUtils的可以下载此包,fileupload组件依赖的commons-io组件

    apache-commons下源码大放送

    apache-commons下全部官方源码和官方API文档,其中有: commons-beanutils-1.8.0 commons-codec commons-collections commons-dbcp commons-dbutils commons-fileupload commons-io commons-lang commons-lang3 ...

    Apache Commons组件简介.ppt

    7. **IO**:Apache Commons IO 提供了一系列IO操作的辅助类,如文件复制、读写、流处理等,使得处理文件和I/O流变得更加简单和可靠。 8. **Net**:Apache Commons Net 提供了网络编程所需的工具类和协议实现,如FTP...

    commons-io-2.4 包含了所有commons-io的jar包和源码

    Apache Commons IO 是一个Java库,专注于提供各种I/O操作的实用工具类,这些操作包括文件、流、过滤器、读写、转换、检测等。在本案例中,我们讨论的是"commons-io-2.4"版本,这个版本包含了完整的Apache Commons IO...

    commons-io-2.11.0.rar

    Apache Commons IO 是一个Java库,专门用于处理输入/输出(I/O)操作。这个库提供了大量的实用工具类,简化了常见的文件、流、过滤器、读写操作等任务。"commons-io-2.11.0.rar"是Apache Commons IO库的版本2.11.0的...

    org.apache.commons.io.FileUtils

    Apache Commons IO库中的`org.apache.commons.io.FileUtils`类是一个非常实用的工具类,它提供了大量方便、高效的方法,用于处理文件系统操作。这个类在Java开发中被广泛使用,尤其是在处理大量的文件读写和管理任务...

    commons-io-2.6.jar

    《Apache Commons IO 2.6在Java开发中的应用详解》 Apache Commons IO是Apache软件基金会的一个开源项目,它提供了一系列实用的I/O操作工具,极大地简化了Java开发中与输入/输出相关的任务。其中,`commons-io-2.6....

    commons-io-2.3.jar

    《Apache Commons IO 2.3.jar:高效便捷的IO操作工具》 Apache Commons IO库是Java开发者必备的工具包之一,尤其是其中的commons-io-2.3.jar版本,它为处理输入/输出流、文件、字符集转换、线程安全的读写操作等...

    commons-io-2.7-bin.zip

    Apache Commons IO 是一个Java库,专门用于处理输入/输出流操作。这个库包含了大量实用类和方法,使得在处理文件、字节流、字符流、过滤器和转换时更加便捷。"commons-io-2.7-bin.zip"是Apache Commons IO库的版本...

    com.springsource.org.apache.commons.io-1.4.0.jar

    jar包分享,你懂的 com.springsource.org.apache.commons.io-1.4.0.jar

    apache.commons全套jar包下载

    1. Apache Commons IO: 这个模块专注于输入/输出操作,如文件处理、流操作、数据转换等。它提供了一系列方便实用的类和方法,比如FileUtils用于文件操作,IOUtils用于流的读写和复制,以及EndianUtils用于处理字节序...

    commons-io-2.2

    《Apache Commons IO 2.2:Java I/O操作的强大工具》 Apache Commons IO库是Java开发者广泛使用的工具库,主要用于处理各种I/O操作。在版本2.2中,这个库提供了一系列精心设计和优化的类和方法,极大地简化了与输入...

Global site tag (gtag.js) - Google Analytics