澳门新浦京8455comJava使用线程池递归压缩文件夹下面的所有子文件

澳门新浦京8455com 11

本文将介绍Java中利用线程池递归的方式压缩文件夹下面的所有子文件,具体方法如下:

主线程等待子线程运行结束再完成的效果如何实现
主线程抛出一个子线程异步处理一些东西,这时主线程要等待子线程运行完成再完成(其实我是为了统计运行时间的)。
这里抛出的子线程可能递归的调用自己,就是再抛一个他的子线程出来,但是到底一共抛多少,事先是不知道的。
应用场景:
1)多线程扫描文件夹内的文件,遇到文件夹内有子文件夹,要递归调用扫描线程的,等到全部扫描完成后,返回结果,显示;
2)多线程快速排序,第一次肯定是单线程的,第一次排序完成后,会分两半,这两半多线程排,递归调用了这个排序线程,这两半很有可能,极大有可能再各分两半,也就是会有4个子线程的子线程再排序。

背景

由于公司的CMS系统里,只接受rar格式压缩的文件,所以没法直接使用nodejs里提供的zip压缩组件。只能从winRar软件入手了,但网上没有多少这方面相关的东西,所以下面也是自己尝试着在做。

主要解决的几个问题

Gzip单个文件压缩

对于单个文件使用GZip压缩。

package date0805.demo1;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.zip.GZIPOutputStream;

public class GZipRunnable implements Runnable {

    private final File file;

    public GZipRunnable(File file) {
        this.file=file;
    }

    @Override
    public void run() {
        if(!file.getName().endsWith(".gz")) {
            File outputFile=new File(file.getParent(),file.getName()+".gz");
            if(!outputFile.exists()) {
                CountTime countTime = new CountTime(file);
                Thread t=new Thread(countTime);
                t.start();
                try(
                    InputStream in =new BufferedInputStream(new FileInputStream(file));
                    OutputStream out=new BufferedOutputStream(new GZIPOutputStream(new FileOutputStream(outputFile)));
                ) {
                    int b;
                    while((b=in.read())!=-1)
                        out.write(b);
                    out.flush();
                } catch(Exception ex) {
                    ex.printStackTrace();
                }
                t.interrupt();
            } else {
                System.out.println(outputFile+"文件已经存在,无法压缩!");
            }
        }
    }
}

我试过网上的那个 CountDownLatch
,但是他只能实现定义好子线程的数量,但是在以上两种情景下,事先你是不知道会有多少个子线程的!

rar软件的位置获取问题

通过node的命令行组件child_process,运行注册表查询命令REG query
‘键值名’,可以查找出相关软件的目录,然后使用正则表达式对结果进行匹配,返回软件目录。命令如下:

var cp=require('child_process');

cp.exec("reg query HKEY_CLASSES_ROOT\WinRAR\shell\open\command /ve", function(e, stdout, stderr) {
                    if(!e){
                        var str = stdout.match(/"([^"]+)"/)[0];
                        if(str){
                            console.log('已经找到winRar程序,详细地址为:'+str);

                        }else{
                            console.log('没有找到winRar程序,无法完成压缩功能!');
                        }
                    }
});

通过上面的命令可以获取到注册表中winRar程序的绝对路径,如果你安装正确应该获得下面这个结果

澳门新浦京8455com 1

创建线程池进行递归压缩

要实现对一个文件夹下面的所有的子文件进行压缩,首先递归找出所有子文件,然后调用压缩线程进行压缩。

package date0805.demo1;

import java.io.File;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class GZipFiles {

    private final static int THREAD_COUNT=4;
    private static ExecutorService service=Executors.newFixedThreadPool(THREAD_COUNT);
    public static void GZip(File fileArgs) {
        if(!fileArgs.isDirectory()) {
            GZipRunnable gZipRunnable = new GZipRunnable(fileArgs);
            service.submit(gZipRunnable);
        }
        else {
            File[] files = fileArgs.listFiles();
            for(File f:files) {
                GZip(f);
            }
        }
    }
    public static void shutdown() {
        service.shutdown();
    }
}

c++里有个WaitForSingleObject,java里有么?

环境变量设置问题

通过上面一步可以解决winRar软件的路径问题,但怎么才能方便地调用rar命令呢?我的第一个想法就是设置环境变量。通过运行命令行命令set
path=%path%;新目录,node命令如下:

cp.exec("set path=%path%"+";c:\", function(e, stdout, stderr) {
            if(!e){
                console.log("环境变量path添加;c:\成功!")
            }
});

获取结果如下

澳门新浦京8455com 2

到这里好像没有问题。其实这里存在一个问题,这个环境变量设置是临时的,在命令行关闭时这个环境变量就消失了。

无法在下一次调用命令行时使用,所以到这里来说,无法再进行下去了。

在调用的过程中,设置环境变量和运行命令是两个步骤:

cp.exec("set path=%path%"+";c:\", function(e, stdout, stderr) {
            if(!e){
                console.log("环境变量path添加;c:\成功!");
                cp.exec('rar a -r f:/build/1.rar f:/xc/gulpfile.js',{encoding:'binary'},function(e,stin,stout){
                    console.log(e,stin,stout);
                });
            }
});

下面的方法运行并不能得到正确的结果。

澳门新浦京8455com 3

压缩提示

package date0805.demo1;

import java.io.File;

public class CountTime implements Runnable {

    private File file;

    public CountTime(File file) {
        super();
        this.file = file;
    }

    @Override
    public void run() {
        System.out.println("正在压缩"+file.getAbsolutePath());
        try {
            while(true) {
                System.out.print(".");
                Thread.sleep(1000);
            }
        }catch (InterruptedException e) {
            System.out.println();
            System.out.println(file.getAbsolutePath()+".gz已经压缩完成!");
        }
    }

}

直接调用winRar程序

澳门新浦京8455com,解决方法有两种,一种是笨方法,让每个使用者,手动去配置环境变量。第二种是直接在命令行使用完整的程序路径调用。

第一种方法,是最后的一步,实在不行才用这个方法,手动毕竟不是程序员该干的事。

第二种方法,测试可以使用,运行以下代码,

cp.exec('"C:\Program Files\WinRAR\WinRAR.exe" a -r f:/build/1.rar f:/xc/gulpfile.js',{encoding:'binary'});

将得到以下结果

澳门新浦京8455com 4澳门新浦京8455com 5

这里需要注意的是当路径中有空格的时候,可以把整个路径用引号包起来,不然会导致命令行命令运行失败。

但这里必须忍受,下面这个窗口。

澳门新浦京8455com 6

测试代码

package date0805.demo1;

import java.io.File;

public class Test {

    public static void main(String[] args) {
        File file=new File("D://新建文件夹");
        GZipFiles.GZip(file);
        GZipFiles.shutdown();
    }
}

一些扩展

1、在压缩的过程中,会包含上级文件夹,如何去压缩只包含文件不包含文件夹的,压缩包形如下面的图示

澳门新浦京8455com 7

2、对文件目录包含的内容进行过滤,并对需要的文件及子文件夹进行压缩,如文件目录如下,我只想压缩里面的htm,txt文件。

澳门新浦京8455com 8

可以通过node的fs组件,然后调用fs.readdirSync()方法,然后对得到的数组进行过滤。

需要注意的问题

1、程序运行路径中有空格时,别忘记用引号把路径包上,以及对符号进行转义。

2、在调用rar压缩文件时,当过滤掉的目录中含有和当前目录相同文件名的文件时,也会把子目录对应的文件压缩进来。(见补充代码)

在文件结构如下

澳门新浦京8455com 9

运行下面的命令

cp.exec('"C:\Program Files\WinRAR\WinRAR.exe" a -r f:/build/1.rar f:/xc/gulp.js',{encoding:'binary'});

虽然在本目录下没有对应的文件,但rar程序会自动,遍历子文件夹,最后生成如下的压缩包

澳门新浦京8455com 10

因为这个组件文件夹里包含gulp.js文件。这会使上面的过滤出现问题,我还没解决,还没想到办法。

总结

基本上面的功能都完成了,只是构建工具里的一小部分,完成对源文件合并,压缩,md5后,添加的一个小功能。后面还会找一资料看一下,怎么解决上面的问题。在这里哪位大侠有解决办法,可以评论或私信,非常感谢。

 

附录一:winRar命令

配置完,winRar的环境变量,直接运行 rar,可以得到下面这些个列表

澳门新浦京8455com 11

使用示例:

rar a contact.rar contact.dat

如果contact.rar不存在将创建contact.rar文件;如果contact.rar压缩包中已有contact.ext,将更新压缩包中的contact.ext

rar a -r -v2000 -sfx vudroid2.rar vudroid2

递归压缩vudroid2目录下全部文件为 2M
大小分卷自解压文件(自解压文件就是压缩文件中已经包含了解压缩的工具,无需用户自己安装解压缩工具)
vudroid2.part1.sfx,vudroid2.part2.rar,vudroid2.part3.rar
等,将命令a换成命令m可将文件压缩后删除

rar x contact.rar

用绝对路径来解压,如果是rar x contact.rar
~/hehe/,前提是hehe文件夹要存在。就是解压到当前路径的hehe目录下,还有一个是e参数,解释是加压到当前目录下,在ubuntu
10.04我实验过,rar e和rar
x都可以用相对路径和绝对路径解压,这一点我也不知道是为什么

rar a -pzaba contact1.rar contact.dat

使用密码 zaba 压缩contact1.rar文件

附录二:node的child_process组件

child_process.exec(command, [options], callback)   来源:《Node.js v4.2.4 手册 & 文档-child_process》

  • command {String} 将要执行的命令,用空格分隔参数
  • options {Object}
    • cwd {String} 子进程的当前工作目录
    • env {Object} 环境变量键值对
    • encoding {String} 编码(缺省为 ‘utf8’)
    • shell {String} 运行命令的 shell(UNIX 上缺省为
      ‘/bin/sh’,Windows 上缺省为 ‘cmd.exe’。该 shell 在 UNIX
      上应当接受 -c 开关,在 Windows 上应当接受 /s /c 开关。在
      Windows 中,命令行解析应当兼容 cmd.exe。)
    • timeout {Number} 超时(缺省为 0)
    • maxBuffer {Number} 最大缓冲(缺省为 200*1024)
    • killSignal {String} 结束信号(缺省为 ‘SIGTERM’)
  • callback {Function} 进程结束时回调并带上输出
    • error {Error}
    • stdout {Buffer}
    • stderr {Buffer}
  • 返回:ChildProcess 对象

在 shell 中执行一个命令并缓冲输出。

child = exec('cat *.js bad_file | wc -l',
  function (error, stdout, stderr) {
    console.log('stdout: ' + stdout);
    console.log('stderr: ' + stderr);
    if (error !== null) {
      console.log('exec error: ' + error);
    }
});

回调参数为 (error, stdout, stderr)。当成功时,error 会是
null。当遇到错误时,error 会是一个Error 实例,并且 err.code
会是子进程的退出代码,同时 err.signal 会被设置为结束进程的信号名。

第二个可选的参数用于指定一些选项,缺省选项为:

{ encoding: 'utf8',
  timeout: 0,
  maxBuffer: 200*1024,
  killSignal: 'SIGTERM',
  cwd: null,
  env: null }

如果 timeout 大于 0,则当进程运行超过 timeout
毫秒后会被终止。子进程使用 killSignal 信号结束(缺省为
'SIGTERM')。maxBuffer 指定了 stdout 或 stderr
所允许的最大数据量,如果超出这个值则子进程会被终止。

补充:

注意问题2的解决方法

对于同文件名,排除的文件夹包含,去除的方法,在命令 开关里。

经过实验可以使用下面这个命名来处理

开关

r0 只递归通配符名称的子目录

rar a -r0 压缩文件名.rar 文件列表
You can leave a response, or trackback from your own site.

Leave a Reply

网站地图xml地图