分享

windows下文件同步 单向同步

 root_gao 2012-09-20

有一个这样的需求,一个源目录A,它的文件是时刻变化的(有程序会不定时的往里仍数据),现在要把A中的数据打包zip,要保证zip中最终打包的数据是最终的数据(当然A可能一直变,意思就是A最近一次变化的数据要能正确的打包到zip中,其实就相当于源和目标的单向同步)。


以下的观点都是基于windows xp,没有特殊说明文件的读写都是用java的流。
首先有几点需要清楚:
1
、在打包之前来判断文件是否打开,其实是没什么意义的。因为有一种可能:在你判断的时候文件还没有打开,但是当你尝试打开时,已经被其他程序打开)
2
、既然A目录是要打包的元数据,往A里仍数据的人就必须要保证仍的数据正确和完整。
3
java中可以多个进程同时打开和写同一个文件只不过先打开的进程T1会等待后打开的进程T2写完数据后,继续写数据。在非文件追加写入的情况 下,T2进程会截断T1之前写入的数据,从新开始写数据,当T2写完后,T1继续在该文件写入。在追加模式下 FileOutputStream("test.txt",true)),则会交叉写入。平常我们用的apache的拷贝函数,都是读源写目标。也就是用的FileOutputStreamJDK的帮助文档中对于此类的描述如下:

文件输出流是用于将数据写入 File FileDescriptor 的输出流。文件是否可用或能否可以被创建取决于基础平台。特别是某些平台一次只允许一个 FileOutputStream(或其他文件写入对象)打开文件进行写入。在这种情况下,如果所涉及的文件已经打开,则此类中的构造方法将失败。

java程序写文件的时候,我们去读一个文件,此时获取到的数据流,将会是读取那一时刻的流,因此打包到zip中的文件可能并不完整
4
windows自身的写文件(比如拷贝一个文件),该文件在写入的过程中,如果用java获取数据流,将会抛出异常。注:文件的共享方式有只读、只写、独占,java可以用FileChannel的文件锁来实现类似的功能。Java中,如果T1在写文件A,此时T2去获取A的文件锁,将会导致T1报文件被占用异常

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;

              }

           }

       }

    } 

--------终于搞完了,欢迎拍砖。--------------

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多