From Gossip@Openhome

Java Gossip: SequenceInputStream

您將一個檔案分割為數個檔案,接下來要將之再度組合還原為原來的檔案,最基本的作法是使用數個 FileInputStream來開啟分割後的檔案,然後一個一個檔案的讀取,並連續寫入至同一個FileOutputStream中,在這中間,您必須 要自行判斷每一個分割檔案的讀取是否完畢,如果完畢就換讀取下一個檔案。

如果您使用SequenceInputStream就不用這麼麻煩,SequenceInputStream可以看作是數個 InputStream物件的組合,當一個InputStream物件的內容讀取完畢後,它就會取出下一個InputStream物件,直到所有的 InputStream物件都讀取完畢為止。

下面這個程式是SequenceInputStream的使用示範,它可以將指定的檔案進行分割,也可以將分割後的檔案還原為一個檔案:

  • SequenceStreamDemo.java
package onlyfun.caterpillar;

import java.util.*;
import java.io.*;

public class SequenceStreamDemo {
public static void main(String[] args) {
try {
// args[0]: 指定分割(s)或連接(c)
switch (args[0].charAt(1)) {
case 's':
// args[1]: 每個分割檔案的大小
int size = Integer.parseInt(args[1]);
// args[2]: 指定要被分割的檔案名稱
seperate(args[2], size);
break;
case 'c':
// args[1]: 指定要被組合的檔案個數
int number = Integer.parseInt(args[1]);
// args[2]: 組合後的檔案名稱
concatenate(args[2], number);
break;
}
}
catch(ArrayIndexOutOfBoundsException e) {
System.out.println(
"Using: java UseSequenceStream [-s/-c]" +
" (size/number) filename");
System.out.println("-s: 分割檔案\n-c: 組合檔案");
}
catch(IOException e) {
e.printStackTrace();
}
}

// 分割檔案
public static void seperate(String filename, int size)
throws IOException {
FileInputStream fileInputStream =
new FileInputStream(new File(filename));
BufferedInputStream bufInputStream =
new BufferedInputStream(fileInputStream);

byte[] data = new byte[1];
int count = 0;
// 從原檔案大小及指定分割的大小
// 決定要分割為幾個檔案
if(fileInputStream.available() % size == 0)
count = fileInputStream.available() / size;
else
count = fileInputStream.available() / size + 1;

// 開始進行分割
for(int i = 0; i < count; i++) {
int num = 0;
// 分割的檔案加上底線與編號
File file = new File(filename + "_" + (i + 1));
BufferedOutputStream bufOutputStream =
new BufferedOutputStream(
new FileOutputStream(file));

while(bufInputStream.read(data) != -1) {
bufOutputStream.write(data);
num++;
if(num == size) { // 分割出一個檔案
bufOutputStream.flush();
bufOutputStream.close();
break;
}
}

if(num < size) {
bufOutputStream.flush();
bufOutputStream.close();
}
}

System.out.println("分割為" + count + "個檔案");
}

// 連接檔案
public static void concatenate(String filename,
int number) throws IOException {
// 收集檔案用的List
List<InputStream> list =
new ArrayList<InputStream>();

for(int i = 0; i < number; i++) {
// 檔案名必須為底線加上編號
File file = new File(filename + "_" + (i+1));
list.add(i, new FileInputStream(file));
}

final Iterator<InputStream> iterator = list.iterator();

// SequenceInputStream 需要一個Enumeration物件來建構
Enumeration<InputStream> enumation =
new Enumeration<InputStream>() {
public boolean hasMoreElements() {
return iterator.hasNext();
}

public InputStream nextElement() {
return iterator.next();
}
};

// 建立SequenceInputStream
// 並使用BufferedInputStream
BufferedInputStream bufInputStream =
new BufferedInputStream(
new SequenceInputStream(enumation),
8192);

BufferedOutputStream bufOutputStream =
new BufferedOutputStream(
new FileOutputStream(filename), 8192);

byte[] data = new byte[1];
// 讀取所有檔案資料並寫入目的地檔案
while(bufInputStream.read(data) != -1)
bufOutputStream.write(data);

bufInputStream.close();
bufOutputStream.flush();
bufOutputStream.close();
System.out.println("組合" + number + "個檔案 OK!!");
}
}

分割檔案時的範例如下:
java onlyfun.caterpillar.SequenceStreamDemo -s 1048576 test.zip
分割為6個檔案


組合檔案時的範例如下:
java onlyfun.caterpillar.SequenceStreamDemo -c 6 test.zip
組合6個檔案 OK!!