有一个这样的需求,一个源目录A,它的文件是时刻变化的(有程序会不定时的往里仍数据),现在要把A中的数据打包zip,要保证zip中最终打包的数据是最终的数据(当然A可能一直变,意思就是A最近一次变化的数据要能正确的打包到zip中,其实就相当于源和目标的单向同步)。
文件输出流是用于将数据写入 File 或 FileDescriptor 的输出流。文件是否可用或能否可以被创建取决于基础平台。特别是某些平台一次只允许一个 FileOutputStream(或其他文件写入对象)打开文件进行写入。在这种情况下,如果所涉及的文件已经打开,则此类中的构造方法将失败。 当java程序写文件的时候,我们去读一个文件,此时获取到的数据流,将会是读取那一时刻的流,因此打包到zip中的文件可能并不完整。 5、为了区分出,哪些文件被正确打包了,我们需要打包完毕后,将正确打包的源文件删除。 6、现在的zip打包,并不直接支持增量打包。因此我们需要将zip中的数据先按照一定的规则(如果该目录下有此文件,说明上次没打完整,以源文件为准。如果没有直接抽取出即可)。
基于以上观点,我们的解决思路如下: 1、将扫描目录中如果有新文件(除了zip),之后判断zip是否存在, 2、存在就解包(解包规则:源目录存在,且zip中也存在的以源为准,zip中存在源不存在的,直接解压,注意此时要用到文件锁,即当你尝试去写一个文件的时候,防止别的java程序也写。)到步骤4 3、不存在,到4 4、压缩源文件,并记录压缩进去的文件的大小。 5、获取当前目录下所有的文件,并逐个与压缩到zip中的文件比较大小,如果一致就把源文件删除(当然也存在一种可能,打完包,判断文件大小的时候文件还没有变,当你尝试去删除文件的时候,文件变了,因此,将采用重命名的方式解决此问题,当删除一个文件之前先重命名此文件,然后比较打包完的文件跟重命名后的文件的大小,如果一致,删除,不一致,重命名回去。
源代码: pack为入口。
/** * zip the directory given its zipFile. * @param src src directory path * @param dest zipFile */ public void pack(String src, String dest) { File destFile = new File(dest); if (destFile.exists()) { //all ready zip exists extractFilesFromZip(src, dest); } //zip Dirctory Map<String, Long> zipedFileMaps = zip(src, dest, false); Iterator<String> iterator = zipedFileMaps.keySet().iterator(); while (iterator.hasNext()) { String key = iterator.next(); File deleteFile = new File(src + File.separator + key); File rename = new File(deleteFile.getAbsolutePath() + ".rename"); if (rename.exists()) { rename.delete(); //maybe still can't delete, then next rename will be fail. } if (deleteFile.renameTo(rename)) { //only for rename success if (rename.length() == zipedFileMaps.get(key).longValue()) { deleteFileOrDirectoryUtilRoot(rename, src); } else { //already changed. we need to raname back rename.renameTo(deleteFile); //maybe still fail.that's:already exists a same name file.we get the new one. } } }
} /** * get zip name given a directory. * @param src directory * @return zip name */ public String getZipName(File src) { //TODO your zip name rule return “yourzipName” }
/** * delete file util reach root or not empty directory. * @param deleteFile * @param root */ public void deleteFileOrDirectoryUtilRoot(File deleteFile, String root) { if (!deleteFile.isDirectory()) { deleteFile.delete(); } File deleteParent = deleteFile.getParentFile(); if (deleteParent != null && deleteParent.listFiles().length == 0 && !deleteParent.getAbsolutePath().equals(root)) { deleteParent.delete(); deleteFileOrDirectoryUtilRoot(deleteFile.getParentFile(), root); } }
/** * extract File from zip. * @param src srcRoot * @param dest zipFile * @param srcFileMap allFilesAndItsLongth * @return extract result. follows will be a false : * 1.not a zip or a corrupt zip * 2.can't get the zip entry and its stream * 3.get the file lock exception */ private boolean extractFilesFromZip(String src, String dest) { boolean result = true; InputStream in = null; RandomAccessFile randomFile = null; FileLock lock = null; try { ZipFile zipFile = new ZipFile(dest, "gbk"); zipFile.getEncoding(); Enumeration e = zipFile.getEntries(); while (e.hasMoreElements()) { ZipEntry zipEntry = (ZipEntry) e.nextElement(); String entryName = new String(zipEntry.getName()); entryName = entryName.replaceAll("\\\\", "/"); if (zipEntry.isDirectory()) { String name = entryName.substring(0, entryName.length() - 1); File f = new File(src + File.separator + name); if (!f.exists()) { f.mkdirs(); } } else { int index = entryName.lastIndexOf("/"); if (index != -1) { File df = new File(src + File.separator + entryName.substring(0, index)); if (!df.exists()) { df.mkdirs(); } } File f = new File(src + File.separator + zipEntry.getName()); if (!f.exists()) { // only when the file not exists now. in = zipFile.getInputStream(zipEntry); randomFile = new RandomAccessFile(f, "rw"); FileChannel channel = randomFile.getChannel(); lock = channel.tryLock(); if (lock != null) { int c; byte[] by = new byte[1024]; while ((c = in.read(by)) != -1) { randomFile.write(by, 0, c); } lock.release(); } randomFile.close(); in.close(); } } } } catch (Exception e) { log.error(e.getMessage()); e.printStackTrace(); result = false; } finally { if (in != null) { try { in.close(); } catch (IOException ex) { log.error(ex.getMessage()); ex.printStackTrace(); result = false; } } if (lock != null && lock.isValid()) { //sometimes lock can't release immediately try { lock.release(); } catch (IOException ex) { log.error(ex.getMessage()); ex.printStackTrace(); result = false; } } if (randomFile != null) { try { randomFile.close(); } catch (IOException ex) { log.error(ex.getMessage()); ex.printStackTrace(); result = false; } } } return result; } /** * zip a directory without .zip and .idoican. * @param src src directory * @param dest zip output file * @param zipCurrentDir is zip current directory */ public Map<String, Long> zip(String src, String dest, boolean zipCurrentDir) { Map<String, Long> rtnMap = new HashMap<String, Long>(); long start = System.currentTimeMillis(); log.info("开始打包:" + dest); ZipOutputStream out = null; try { File outFile = new File(dest); out = new ZipOutputStream(outFile); out.setEncoding("gbk"); File fileOrDirectory = new File(src); if (zipCurrentDir) { zipFileOrDirectory(out, fileOrDirectory, "", rtnMap); } else { File[] files = fileOrDirectory.listFiles(); if (files != null) { for (int i = 0; i < files.length; i++) { zipFileOrDirectory(out, files[i], "", rtnMap); } } } log.info("打包" + src + "到" + dest + "成功!,耗时:" + (System.currentTimeMillis() - start) + "ms"); } catch (Exception ex) { rtnMap = new HashMap<String, Long>(); //when exception that is not a correct zip ,so not delete file source. log.error(ex.getMessage()); } finally { if (out != null) { try { out.close(); } catch (IOException ex) { log.error(ex.getMessage()); } } } return rtnMap; }
/** * recursive zip a direcotry. * @param out zip output stream * @param fileOrDirectory file to zip * @param curPath relative path * @param rtnMap ziped file and its length * @throws Exception */ private void zipFileOrDirectory(ZipOutputStream out, File fileOrDirectory, String curPath, Map<String, Long> rtnMap) throws Exception { FileInputStream in = null; try { if (!fileOrDirectory.isDirectory() && !fileOrDirectory.getName().toLowerCase().endsWith(".zip")) {
// 压缩文件 byte[] buffer = new byte[1024]; int bytes_read; long allBytes = 0; in = new FileInputStream(fileOrDirectory); ZipEntry entry = new ZipEntry(curPath + fileOrDirectory.getName()); out.putNextEntry(entry); while ((bytes_read = in.read(buffer)) != -1) { allBytes += bytes_read; out.write(buffer, 0, bytes_read); } rtnMap.put(curPath + fileOrDirectory.getName(), allBytes); out.closeEntry(); } else { // 压缩目录 File[] entries = fileOrDirectory.listFiles(); if (entries != null && entries.length > 0) { for (int i = 0; i < entries.length; i++) { // 递归压缩,更新curPaths zipFileOrDirectory(out, entries[i], curPath + fileOrDirectory.getName() + File.separator, rtnMap); } } else { //for no subFiles directory if (!fileOrDirectory.getName().toLowerCase().endsWith(".zip") && !fileOrDirectory.getName().toLowerCase().endsWith(".idoican")) { ZipEntry entry = new ZipEntry(curPath + fileOrDirectory.getName() + File.separator); out.putNextEntry(entry); out.closeEntry(); } } } } catch (IOException ex) { log.error(ex.getMessage()); throw ex; } catch (Exception e) { log.error(e.getMessage()); throw e; } finally { if (in != null) { try { in.close(); } catch (IOException ex) { log.error(ex.getMessage()); throw ex; } } } } --------终于搞完了,欢迎拍砖。-------------- |
|