首先设计归档的协议
文件归档与解归档
运用IO流技术,把若干文件以流的方式写到一个文件里去,但是要区分文件和文件之间的边界,就需要设计每个文件的“头”。文件在磁盘上以字节数组的方式存在。“头”包含文件的扩展名类型(1个字节),文件的长度(4个字节),文件内容。解归档:先读文件类型,再读文件长度,接着读文件长度个字节。
增强版:文件名长度(1个字节长度),文件名字节数组(字符串解码getBytes),文件长度(4个字节),文件内容。
代码实现归档与解档:
//Archiver.java
1 import java.io.FileInputStream;
2 import java.io.FileOutputStream;
3 import java.io.IOException;
4
5 public class Archiver {
6 public void newArchiverFile(String[] srcPaths, String yarPath) {
7 FileOutputStream fout = null;
8 try {
9 //创建yar归档文件的输出流
10 fout = new FileOutputStream(yarPath);
11 for(String srcPath : srcPaths) {
12 addFile(srcPath, fout);
13
14 }
15 } catch (Exception e) {
16 e.printStackTrace();
17 } finally {
18 if (fout != null) {
19 try {
20 fout.close();
21 } catch (IOException e) {
22 e.printStackTrace();
23 }
24 }
25 }
26 }
27
28 /**
29 * 向yar归档文件中添加文件
30 * @param srcPath
31 * @param fout
32 */
33 private void addFile(String srcPath, FileOutputStream fout) {
34 FileInputStream fin = null;
35 try {
36 //1. 取出文件类型
37 int fType = getFileType(srcPath);
38
39 //2. 取出文件的长度
40 fin = new FileInputStream(srcPath);
41 int length = fin.available();
42
43 //3. 将类型写入fout
44 byte bFType = (byte) fType;
45 fout.write(new byte[] {bFType});
46
47 //4. 将长度写入yar中
48 byte[] bytes = Int2ByteArr(length);
49 fout.write(bytes);
50
51 //5. 写入文件内容
52 int len = -1;
53 byte[] buffer = new byte[1024];
54 while((len = fin.read(buffer)) != -1) {
55 fout.write(buffer, 0, len);
56 }
57
58 } catch (Exception e) {
59
60 e.printStackTrace();
61 } finally {
62 if (fin != null) {
63 try {
64 fin.close();
65 } catch (IOException e) {
66 e.printStackTrace();
67 }
68 }
69 }
70
71 }
72
73 private byte[] Int2ByteArr(int i) {
74 byte[] bytes = new byte[4];
75 bytes[0] = (byte) i;
76 bytes[1] = (byte) (i >> 8);
77 bytes[2] = (byte) (i >> 16);
78 bytes[3] = (byte) (i >> 24);
79 return bytes;
80 }
81
82 /**
83 * 得到文件类型
84 * 0-txt
85 * 1-jpg
86 * 2-avi
87 * 3-gif
88 * 4-exe
89 * 5-mp4
90 * @param srcPath
91 * @return
92 */
93 private int getFileType(String srcPath) {
94 String ext = srcPath.substring(srcPath.lastIndexOf(".")).toLowerCase();
95 int type = -1;
96 if (".txt".equals(ext)) {
97 type = 0;
98 } else if (".jpg".equals(ext)) {
99 type = 1;
100 } else if (".avi".equals(ext)) {
101 type = 2;
102 } else if (".gif".equals(ext)) {
103 type = 3;
104 } else if (".exe".equals(ext)) {
105 type = 4;
106 } else if (".mp4".equals(ext)) {
107 type = 5;
108 } else {
109 type = -1;
110 }
111
112 return type;
113 }
114
115 /**
116 * 向原有yar中添加新文件
117 */
118 public void addFile(String srcPath, String yarPath) {
119 try {
120 FileOutputStream fos = new FileOutputStream(yarPath,true);//追加
121 addFile(srcPath, fos);
122 fos.close();
123 } catch (Exception e) {
124 e.printStackTrace();
125 }
126 }
127
128 /**
129 * 解档文件
130 */
131 public void unarchive(String yarPath, String destDir) {
132 try {
133 FileInputStream fis = new FileInputStream(yarPath);
134 int i = 1;
135 //循环读取下一个文件
136 while (readNextFile(destDir, (i++) + "", fis)) {
137
138 }
139 fis.close();
140 } catch (Exception e) {
141 e.printStackTrace();
142 }
143
144 }
145
146 private boolean readNextFile(String destDir,String name, FileInputStream fis) {
147 try {
148 //文件类型
149 int type = fis.read();
150 //文件的扩展名
151 String ext = getFileExt(type);
152 if (type == -1) {
153 return false;
154 }
155
156 //构造文件
157 FileOutputStream fout = new FileOutputStream(destDir + "/" + name + ext);
158
159 // 1.读取文件长度
160 byte[] bytes = new byte[4];
161 fis.read(bytes);
162
163 // 2.转换字节数组为int
164 int fileLength = byteArr2Int(bytes);
165
166 // 3.读取文件
167 byte[] buffer = new byte[1024];
168
169 // 计算读取文件的循环次数
170 int count = 0;
171 if (fileLength / buffer.length == 0) {
172 count = fileLength / buffer.length;
173 }
174 else {
175 count = fileLength / buffer.length + 1;
176 }
177 //开始循环读取
178 for (int i = 0; i < count; i++) {
179 if (i != (count - 1)) {
180 fis.read(buffer);
181 fout.write(buffer);
182 }
183 else {
184 byte[] buf0 = new byte[fileLength - ((count - 1) * buffer.length)];
185 fis.read(buf0);
186 fout.write(buf0);
187 }
188 }
189 fout.close();
190 return true;
191 } catch (Exception e) {
192 e.printStackTrace();
193 }
194
195 return false;
196 }
197
198 private String getFileExt(int type) {
199 String ext = ".tmp";
200 switch (type) {
201 case 0:
202 ext = ".txt";
203 break;
204 case 1:
205 ext = ".jpg";
206 break;
207 case 2:
208 ext = ".avi";
209 break;
210 case 3:
211 ext = ".gif";
212 break;
213 case 4:
214 ext = ".exe";
215 break;
216 case 5:
217 ext = ".mp4";
218 break;
219 default:
220 ext = ".tmp";
221 break;
222 }
223 return ext;
224 }
225
226 /**
227 * 将长度为4的字节数组转换成int
228 * @param bytes
229 * @return
230 */
231 private int byteArr2Int(byte[] bytes) {
232 //int i = (int)(bytes[3] << 24 | bytes[2] << 16 | bytes[1] << 8 | bytes[0]);
233 int i0 = (bytes[3] & 0xff) << 24;
234 int i1 = (bytes[2] & 0xff) << 16;
235 int i2 = (bytes[1] & 0xff) << 8;
236 int i3 = (bytes[0] & 0xff);
237 return i0 | i1 | i2 | i3;
238 }
239 }
测试代码:
//App.java
1 import org.junit.Test;
2
3 public class App {
4
5 /**
6 * 新建归档文件
7 */
8 @Test
9 public void newArchiveFile() {
10 Archiver archiver = new Archiver();
11 String[] srcPaths = { "D:/arch/Sax.JPG", "D:/arch/1.mp4", "D:/arch/java笔记.txt"};
12 String yarPath = "d:/arch/myYar.yar";
13 archiver.newArchiverFile(srcPaths, yarPath);
14 System.out.println("over");
15 }
16
17 /**
18 * 向原有归档文件中添加新文件
19 */
20 @Test
21 public void addFile(){
22 Archiver archiver = new Archiver();
23 archiver.addFile("D:/arch/hello.txt", "d:/arch/myYar.yar");
24 }
25
26 /**
27 * 解档文件
28 */
29 @Test
30 public void unarchiveFile() {
31 Archiver archiver = new Archiver();
32 archiver.unarchive("d:/arch/myYar.yar", "d:/arch/unarch");
33 }
34
35 }
newArchiveFile()在D:\arch目录下生成了myYar.yar归档文件大小为1.MP4 java笔记.txt Sax.jpg三个文件之和加15字节
unarchiveFile()之后在d:/arch/unarch下还原文件