FileMappedInputStream: 2GB以上のファイルに対応
MappedByteBuffer + InputStream: ファイルにマッピングされたランダムアクセス可能な入力ストリーム - sileの日記で作成したクラスの巨大ファイル対応版。
2GB以上のファイルでも扱うことが可能。
import java.io.IOException; import java.io.InputStream; import java.io.FileInputStream; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; public class FileMappedInputStream extends InputStream { private final ByteBuffer[] maps; private int cur = 0; private int end_map; public FileMappedInputStream(String filepath) throws IOException { final FileChannel cnl = new FileInputStream(filepath).getChannel(); try { final int map_num = (int)(cnl.size()/Integer.MAX_VALUE)+1; maps = new ByteBuffer[map_num]; for(int i=0; i < map_num; i++) { final int offset = Integer.MAX_VALUE*i; final int length = (int)Math.min(Integer.MAX_VALUE, cnl.size()-offset); maps[i] = cnl.map(FileChannel.MapMode.READ_ONLY, offset, length); } end_map = maps.length; } finally { cnl.close(); } } private FileMappedInputStream(FileMappedInputStream src) { cur = src.cur; end_map = src.end_map; maps = new ByteBuffer[src.maps.length]; for(int i=0; i < maps.length; i++) // ByteBuffer複製: // - メモリ領域は共有されたまま // - 位置情報(position,limit)等は複製先と複製元で別に保持される maps[i] = src.maps[i].duplicate(); } private boolean eos() { if(end_map == cur) return true; if(maps[cur].hasRemaining()==false) { cur++; return eos(); } return false; } public InputStream range(long start, int length) throws IOException { final int map_n = (int)(start/Integer.MAX_VALUE); final long offset = start%Integer.MAX_VALUE; final long limit = offset+length; cur = map_n; maps[cur].clear().position((int)offset); if(limit < Integer.MAX_VALUE) { maps[cur].limit((int)limit); end_map = cur+1; } else { maps[cur].limit(maps[cur].capacity()); maps[cur+1].clear().limit((int)(limit-Integer.MAX_VALUE)); end_map = cur+2; } return this; } // 複数の範囲から平行して入力を行ないたい場合用のメソッド。 // // 第三引数(copy)にtrueを渡した場合に返されるインスタンスは、 // 読み込み位置情報などを他と共有することがないので、安全。 public InputStream range(long start, int length, boolean copy) throws IOException { if(copy) return new FileMappedInputStream(this).range(start, length); else return range(start, length); } @Override public int read() throws IOException { if(eos()) return -1; return (int)(maps[cur].get())&0xFF; } @Override public int read(byte[] bytes, int offset, int length) throws IOException { if(eos()) return -1; if(length < maps[cur].remaining()) { maps[cur].get(bytes, offset, length); return length; } else { final int len = maps[cur].remaining(); maps[cur].get(bytes, offset, len); if(eos()) return len; return len + read(bytes, offset+len, length-len); } } @Override public int read(byte[] bytes) throws IOException { return read(bytes, 0, bytes.length); } }