首先设计归档的协议
文件归档与解归档
运用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下还原文件