java快速分割及合併檔案
檔案分割與合併是一個常見需求,比如:上傳大檔案時,可以先分割成小塊,傳到伺服器後,再進行合併。很多高大上的分散式檔案系統(比如:google的GFS、taobao的TFS)裡,也是按block為單位,對檔案進行分割或合併。
看下基本思路:
如果有一個大檔案,指定分割大小後(比如:按1M切割)
step 1:
先根據原始檔案大小、分割大小,算出最終分割的小檔案數N
step 2:
在磁碟上建立這N個小檔案
step 3:
開多個執行緒(執行緒數=分割檔案數),每個執行緒裡,利用RandomAccessFile的seek功能,將讀取指標定位到原檔案裡每一段的段首位置,然後向後讀取指定大小(即:分割塊大小),最終寫入對應的分割檔案,因為多執行緒並行處理,各寫各的小檔案,速度相對還是比較快的。
合併時,把上面的思路逆向處理即可。
核心程式碼:
分割處理:
1 /** 2 * 拆分檔案 3 * @param fileName 待拆分的完整檔名 4 * @param byteSize 按多少位元組大小拆分 5 * @return 拆分後的檔名列表 6 * @throws IOException 7 */ 8 public ListsplitBySize(String fileName, int byteSize) 9 throws IOException { 10 List parts = new ArrayList (); 11 File file = new File(fileName); 12 int count = (int) Math.ceil(file.length() / (double) byteSize); 13 int countLen = (count + "").length(); 14 ThreadPoolExecutor threadPool = new ThreadPoolExecutor(count, 15 count * 3, 1, TimeUnit.SECONDS, 16 new ArrayBlockingQueue (count * 2)); 17 18 for (int i = 0; i 1 private class SplitRunnable implements Runnable { 2 int byteSize; 3 String partFileName; 4 File originFile; 5 int startPos; 6 7 public SplitRunnable(int byteSize, int startPos, String partFileName, 8 File originFile) { 9 this.startPos = startPos; 10 this.byteSize = byteSize; 11 this.partFileName = partFileName; 12 this.originFile = originFile; 13 } 14 15 public void run() { 16 RandomAccessFile rFile; 17 OutputStream os; 18 try { 19 rFile = new RandomAccessFile(originFile, "r"); 20 byte[] b = new byte[byteSize]; 21 rFile.seek(startPos);// 移動指標到每“段”開頭 22 int s = rFile.read(b); 23 os = new FileOutputStream(partFileName); 24 os.write(b, 0, s); 25 os.flush(); 26 os.close(); 27 } catch (IOException e) { 28 e.printStackTrace(); 29 } 30 } 31 }合併處理:
1 /** 2 * 合併檔案 3 * 4 * @param dirPath 拆分檔案所在目錄名 5 * @param partFileSuffix 拆分檔案字尾名 6 * @param partFileSize 拆分檔案的位元組數大小 7 * @param mergeFileName 合併後的檔名 8 * @throws IOException 9 */ 10 public void mergePartFiles(String dirPath, String partFileSuffix, 11 int partFileSize, String mergeFileName) throws IOException { 12 ArrayListpartFiles = FileUtil.getDirFiles(dirPath, 13 partFileSuffix); 14 Collections.sort(partFiles, new FileComparator()); 15 16 RandomAccessFile randomAccessFile = new RandomAccessFile(mergeFileName, 17 "rw"); 18 randomAccessFile.setLength(partFileSize * (partFiles.size() - 1) 19 + partFiles.get(partFiles.size() - 1).length()); 20 randomAccessFile.close(); 21 22 ThreadPoolExecutor threadPool = new ThreadPoolExecutor( 23 partFiles.size(), partFiles.size() * 3, 1, TimeUnit.SECONDS, 24 new ArrayBlockingQueue (partFiles.size() * 2)); 25 26 for (int i = 0; i 1 private class MergeRunnable implements Runnable { 2 long startPos; 3 String mergeFileName; 4 File partFile; 5 6 public MergeRunnable(long startPos, String mergeFileName, File partFile) { 7 this.startPos = startPos; 8 this.mergeFileName = mergeFileName; 9 this.partFile = partFile; 10 } 11 12 public void run() { 13 RandomAccessFile rFile; 14 try { 15 rFile = new RandomAccessFile(mergeFileName, "rw"); 16 rFile.seek(startPos); 17 FileInputStream fs = new FileInputStream(partFile); 18 byte[] b = new byte[fs.available()]; 19 fs.read(b); 20 fs.close(); 21 rFile.write(b); 22 rFile.close(); 23 } catch (IOException e) { 24 e.printStackTrace(); 25 } 26 } 27 }為了方便檔案操作,把關於檔案讀寫的功能,全封裝到FileUtil類:
1 package com.cnblogs.yjmyzz; 2 3 import java.io.*; 4 import java.util.*; 5 import java.util.concurrent.*; 6 7 /** 8 * 檔案處理輔助類 9 * 10 * @author yjmyzz@126.com 11 * @version 0.2 12 * @since 2014-11-17 13 * 14 */ 15 public class FileUtil { 16 17 /** 18 * 當前目錄路徑 19 */ 20 public static String currentWorkDir = System.getProperty("user.dir") + "\"; 21 22 /** 23 * 左填充 24 * 25 * @param str 26 * @param length 27 * @param ch 28 * @return 29 */ 30 public static String leftPad(String str, int length, char ch) { 31 if (str.length() >= length) { 32 return str; 33 } 34 char[] chs = new char[length]; 35 Arrays.fill(chs, ch); 36 char[] src = str.toCharArray(); 37 System.arraycopy(src, 0, chs, length - src.length, src.length); 38 return new String(chs); 39 40 } 41 42 /** 43 * 刪除檔案 44 * 45 * @param fileName 46 * 待刪除的完整檔名 47 * @return 48 */ 49 public static boolean delete(String fileName) { 50 boolean result = false; 51 File f = new File(fileName); 52 if (f.exists()) { 53 result = f.delete(); 54 55 } else { 56 result = true; 57 } 58 return result; 59 } 60 61 /*** 62 * 遞迴獲取指定目錄下的所有的檔案(不包括資料夾) 63 * 64 * @param obj 65 * @return 66 */ 67 public static ArrayListgetAllFiles(String dirPath) { 68 File dir = new File(dirPath); 69 70 ArrayList files = new ArrayList (); 71 72 if (dir.isDirectory()) { 73 File[] fileArr = dir.listFiles(); 74 for (int i = 0; i getDirFiles(String dirPath) { 93 File path = new File(dirPath); 94 File[] fileArr = path.listFiles(); 95 ArrayList files = new ArrayList (); 96 97 for (File f : fileArr) { 98 if (f.isFile()) { 99 files.add(f); 100 } 101 } 102 return files; 103 } 104 105 /** 106 * 獲取指定目錄下特定檔案字尾名的檔案列表(不包括子資料夾) 107 * 108 * @param dirPath 109 * 目錄路徑 110 * @param suffix 111 * 檔案字尾 112 * @return 113 */ 114 public static ArrayList getDirFiles(String dirPath, 115 final String suffix) { 116 File path = new File(dirPath); 117 File[] fileArr = path.listFiles(new FilenameFilter() { 118 public boolean accept(File dir, String name) { 119 String lowerName = name.toLowerCase(); 120 String lowerSuffix = suffix.toLowerCase(); 121 if (lowerName.endsWith(lowerSuffix)) { 122 return true; 123 } 124 return false; 125 } 126 127 }); 128 ArrayList files = new ArrayList (); 129 130 for (File f : fileArr) { 131 if (f.isFile()) { 132 files.add(f); 133 } 134 } 135 return files; 136 } 137 138 /** 139 * 讀取檔案內容 140 * 141 * @param fileName 142 * 待讀取的完整檔名 143 * @return 檔案內容 144 * @throws IOException 145 */ 146 public static String read(String fileName) throws IOException { 147 File f = new File(fileName); 148 FileInputStream fs = new FileInputStream(f); 149 String result = null; 150 byte[] b = new byte[fs.available()]; 151 fs.read(b); 152 fs.close(); 153 result = new String(b); 154 return result; 155 } 156 157 /** 158 * 寫檔案 159 * 160 * @param fileName 161 * 目標檔名 162 * @param fileContent 163 * 寫入的內容 164 * @return 165 * @throws IOException 166 */ 167 public static boolean write(String fileName, String fileContent) 168 throws IOException { 169 boolean result = false; 170 File f = new File(fileName); 171 FileOutputStream fs = new FileOutputStream(f); 172 byte[] b = fileContent.getBytes(); 173 fs.write(b); 174 fs.flush(); 175 fs.close(); 176 result = true; 177 return result; 178 } 179 180 /** 181 * 追加內容到指定檔案 182 * 183 * @param fileName 184 * @param fileContent 185 * @return 186 * @throws IOException 187 */ 188 public static boolean append(String fileName, String fileContent) 189 throws IOException { 190 boolean result = false; 191 File f = new File(fileName); 192 if (f.exists()) { 193 RandomAccessFile rFile = new RandomAccessFile(f, "rw"); 194 byte[] b = fileContent.getBytes(); 195 long originLen = f.length(); 196 rFile.setLength(originLen + b.length); 197 rFile.seek(originLen); 198 rFile.write(b); 199 rFile.close(); 200 } 201 result = true; 202 return result; 203 } 204 205 /** 206 * 拆分檔案 207 * 208 * @param fileName 209 * 待拆分的完整檔名 210 * @param byteSize 211 * 按多少位元組大小拆分 212 * @return 拆分後的檔名列表 213 * @throws IOException 214 */ 215 public List splitBySize(String fileName, int byteSize) 216 throws IOException { 217 List parts = new ArrayList (); 218 File file = new File(fileName); 219 int count = (int) Math.ceil(file.length() / (double) byteSize); 220 int countLen = (count + "").length(); 221 ThreadPoolExecutor threadPool = new ThreadPoolExecutor(count, 222 count * 3, 1, TimeUnit.SECONDS, 223 new ArrayBlockingQueue (count * 2)); 224 225 for (int i = 0; i partFiles = FileUtil.getDirFiles(dirPath, 251 partFileSuffix); 252 Collections.sort(partFiles, new FileComparator()); 253 254 RandomAccessFile randomAccessFile = new RandomAccessFile(mergeFileName, 255 "rw"); 256 randomAccessFile.setLength(partFileSize * (partFiles.size() - 1) 257 + partFiles.get(partFiles.size() - 1).length()); 258 randomAccessFile.close(); 259 260 ThreadPoolExecutor threadPool = new ThreadPoolExecutor( 261 partFiles.size(), partFiles.size() * 3, 1, TimeUnit.SECONDS, 262 new ArrayBlockingQueue (partFiles.size() * 2)); 263 264 for (int i = 0; i { 278 public int compare(File o1, File o2) { 279 return o1.getName().compareToIgnoreCase(o2.getName()); 280 } 281 } 282 283 /** 284 * 分割處理Runnable 285 * 286 * @author yjmyzz@126.com 287 * 288 */ 289 private class SplitRunnable implements Runnable { 290 int byteSize; 291 String partFileName; 292 File originFile; 293 int startPos; 294 295 public SplitRunnable(int byteSize, int startPos, String partFileName, 296 File originFile) { 297 this.startPos = startPos; 298 this.byteSize = byteSize; 299 this.partFileName = partFileName; 300 this.originFile = originFile; 301 } 302 303 public void run() { 304 RandomAccessFile rFile; 305 OutputStream os; 306 try { 307 rFile = new RandomAccessFile(originFile, "r"); 308 byte[] b = new byte[byteSize]; 309 rFile.seek(startPos);// 移動指標到每“段”開頭 310 int s = rFile.read(b); 311 os = new FileOutputStream(partFileName); 312 os.write(b, 0, s); 313 os.flush(); 314 os.close(); 315 } catch (IOException e) { 316 e.printStackTrace(); 317 } 318 } 319 } 320 321 /** 322 * 合併處理Runnable 323 * 324 * @author yjmyzz@126.com 325 * 326 */ 327 private class MergeRunnable implements Runnable { 328 long startPos; 329 String mergeFileName; 330 File partFile; 331 332 public MergeRunnable(long startPos, String mergeFileName, File partFile) { 333 this.startPos = startPos; 334 this.mergeFileName = mergeFileName; 335 this.partFile = partFile; 336 } 337 338 public void run() { 339 RandomAccessFile rFile; 340 try { 341 rFile = new RandomAccessFile(mergeFileName, "rw"); 342 rFile.seek(startPos); 343 FileInputStream fs = new FileInputStream(partFile); 344 byte[] b = new byte[fs.available()]; 345 fs.read(b); 346 fs.close(); 347 rFile.write(b); 348 rFile.close(); 349 } catch (IOException e) { 350 e.printStackTrace(); 351 } 352 } 353 } 354 355 } 單元測試:
1 package com.cnblogs.yjmyzz; 2 3 import java.io.IOException; 4 5 import org.junit.Test; 6 7 public class FileTest { 8 9 @Test 10 public void writeFile() throws IOException, InterruptedException { 11 12 System.out.println(FileUtil.currentWorkDir); 13 14 StringBuilder sb = new StringBuilder(); 15 16 long originFileSize = 1024 * 1024 * 100;// 100M 17 int blockFileSize = 1024 * 1024 * 15;// 15M 18 19 // 生成一個大檔案 20 for (int i = 0; i
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/4369/viewspace-2800452/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- shell 檔案合併 去重 分割
- Pandas之EXCEL資料讀取/儲存/檔案分割/檔案合併Excel
- 多個excel檔案合併成一個excel表的方法 如何快速合併多個excel檔案Excel
- Python批次分割Excel後逐行做差、合併檔案的方法PythonExcel
- 使用 7-Zip 進行檔案分割與合併(命令列示例)命令列
- Linux下合併檔案Linux
- Flink SQL FileSystem Connector 分割槽提交與自定義小檔案合併策略 SQL
- 影片合併分割軟體如何合併影片
- Python之合併PDF檔案Python
- win10分割槽合併保留資料怎麼操作_win10硬碟合併分割槽並保留檔案詳細步驟Win10硬碟
- git合併分支,如果選擇性的合併檔案?Git
- linux檔案合併、去重、拆分Linux
- Python合併多個csv檔案Python
- 資料檔案合併與拆分
- 辦公自動化:PDF檔案合併器,將多個PDF檔案進行合併
- win10怎麼合併分割槽_win10合併分割槽的方法Win10
- 多個 EXCEL 檔案如何合併成一個檔案Excel
- 合併PDF檔案怎樣做?分享兩種PDF合併方法
- mac分割槽合併APFS容器Mac
- Linux 磁碟分割槽及檔案系統管理Linux
- 基於檔案的表合併及行轉列實現參考
- 一款免費使用的PDF檔案批次合併工具,可操作pdf檔案順序,按順序合併pdf檔案。
- 一款免費使用的PDF檔案批量合併工具,可操作pdf檔案順序,按順序合併pdf檔案。
- 關於numpy的索引、合併、分割索引
- win10u盤分割槽怎麼合併 win10如何把U分割槽合併Win10
- PDF多檔案合併怎麼完成?PDF合併的簡單方法分享
- Hive表小檔案合併方法總結Hive
- git合併單個檔案到其他分支Git
- Git 合併指定檔案或資料夾Git
- iceberg合併小檔案衝突測試
- pdf檔案合併工具(abelssoft easy pdf 2020)
- Git 教程:解密 .gitignore 檔案、合併分支、解決衝突、及 Git 幫助Git解密
- windows10快速批量刪檔案及子檔案的方法Windows
- java 合併pdfJava
- 影片合併分割軟體如何剪下影片
- Spark優化之小檔案是否需要合併?Spark優化
- 如何在 Acrobat Pro DC 與其它檔案合併建立單個 PDF 檔案?BAT
- Python合併多個Excel檔案中的指定sheetPythonExcel