1、问题描述

公司项目有上传2g压缩包(zip、rar)功能,其中zip压缩包功能直接使用linux命令解压没有问题,在RAR解压时恳求出现阻塞卡死的情况(直到情断恳求超时linux系统界面,恳请就会失败)linux的压缩命令,查看服务器日志解压命令如下:

/usr/local/bin/unrar  X -o+ /data/temp/upload/xxx/2021/06/01/xxx_1622544204569.rar /data/temp/upload/xxx/2021/06/01

可以看出解压命令是没有错误的,于是去查看解压命令是否执行成功,找到解压目录,文件早已解压成功,如右图:

这么问题来了,为何解压成功后linux的压缩命令,程序卡死在解压的代码(Processproc=Runtime.getRuntime().exec(cmd))??解压的方式如下:

/**
     * 采用命令行方式解压文件
     *
     * @param zipFile 压缩文件
     * @param destDir 解压结果路径
     * @return
     */
    public static boolean realExtract(String zipFile, String destDir) {
        // 解决路径中存在/..格式的路径问题
        destDir = new File(destDir).getAbsoluteFile().getAbsolutePath();
        while (destDir.contains("..")) {
            String[] sepList = destDir.split("\\");
            destDir = "";
            for (int i = 0; i < sepList.length; i++) {
                if (!"..".equals(sepList[i]) && i < sepList.length - 1 && "..".equals(sepList[i + 1])) {
                    i++;
                } else {
                    destDir += sepList[i] + File.separator;
                }
            }
        }
        boolean bool = false;
        if (!FileUtil.exist(zipFile)) {
            return false;
        }
        if (!FileUtil.exist(destDir)) {
            FileUtil.mkdir(destDir);
        }
        //判断系统类型 开始调用命令行解压,参数-o+是表示覆盖的意思
        String cmdPath = "";
        String osName = System.getProperty("os.name");
        if (osName.contains("Window")) {
            cmdPath = "C:\Program Files\WinRAR\WinRAR.exe";
        } else {
            cmdPath = "/usr/local/bin/unrar";
        }
        String cmd = cmdPath + " X -o+ " + zipFile + " " + destDir;
        System.out.println(cmd);
        try {
        
            Process proc = Runtime.getRuntime().exec(cmd);
            if (proc.waitFor() != 0) {
                if (proc.exitValue() == 0) {
                    bool = false;
                }
            } else {
                bool = true;
            }
        } catch (Exception e) {
           log.error("解压失败:" + e.getMessage(), e);
            e.printStackTrace();
        }
        System.out.println("解压" + (bool ? "成功" : "失败"));
        return bool;
    }

问题剖析

1、排除服务器权限问题(zip文件可解压成功,程序正常运行)
2、排除RAR解压命令问题(压缩包已经正常解压)
3、那现在就只剩下代码问题了
	由于系统是接手的项目,工具类在别的代码中也有引用,就直接拿过来使用,理所当然的认为代码没问题,于是开始找自己的问题(服务器目录权限、服务器上RAR版本等都没找到),准备在服务器上生成程序的dump日志,在查看服务器进程时发现了新大陆,如下图:
	
![在这里插入图片描述](https://img-blog.csdnimg.cn/2021060213295942.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3d1aGFuY2hlbm1pbg==,size_16,color_FFFFFF,t_70#pic_center)

这个是解压RAR文件的进程,那问题可以确定了,也就是RAR解压没有关掉进程,那我看下JAVA执行linux命令形式:

Process proc = Runtime.getRuntime().exec(cmd)

百度下Process在线API:

重点我们看下边的图:

听到这儿马起来对比代码:

这个缺乏了杀害子进程的代码,那我们把代码更改下,下边是ZIP和RAR解压的工具类:

import cn.hutool.core.io.FileUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.tools.zip.ZipEntry;
import org.apache.tools.zip.ZipFile;
import java.io.*;
import java.util.Enumeration;
@Slf4j
public class ZipAndRarTools {
   
    /**
     * 解压Zip文件
     *
     * @param zipFileName  需要解压缩的文件位置
     * @param descFileName 将文件解压到某个路径
     * @param isDel        是否删除压缩包
     * @throws IOException
     */
    public static void unZip(String zipFileName, String descFileName, boolean isDel) throws IOException {
        System.out.println("文件解压开始...");
        String descFileNames = descFileName;
        if (!descFileNames.endsWith(File.separator)) {
            descFileNames = descFileNames + File.separator;
        }
        ZipFile zipFile = null;
        try {
            zipFile = new ZipFile(zipFileName);
            ZipEntry entry = null;
            String entryName = null;
            String descFileDir = null;
            byte[] buf = new byte[4096];
            int readByte = 0;
            @SuppressWarnings("rawtypes")
            Enumeration enums = zipFile.getEntries();
            while (enums.hasMoreElements()) {
                entry = (ZipEntry) enums.nextElement();
                entryName = entry.getName();
                descFileDir = descFileNames + entryName;
                if (entry.isDirectory()) {
                    new File(descFileDir).mkdir();
                    continue;
                } else {
                    new File(descFileDir).getParentFile().mkdir();
                }
                File file = new File(descFileDir);
                OutputStream os = new FileOutputStream(file);
                InputStream is = zipFile.getInputStream(entry);
                while ((readByte = is.read(buf)) != -1) {
                    os.write(buf, 0, readByte);
                }
                os.close();
                is.close();
            }
            System.out.println("文件解压成功!");
        } catch (Exception e) {
            System.out.println("文件解压失败!");
            e.printStackTrace();
        } finally {
            zipFile.close();
        }
        //删除压缩包
        if (isDel) {
            FileUtil.del(zipFileName);
        }
    }
    /**
     * 采用命令行方式解压文件
     *
     * @param zipFile 压缩文件
     * @param destDir 解压结果路径
     * @return
     */
    public static boolean realExtract(String zipFile, String destDir) {
        // 解决路径中存在/..格式的路径问题
        destDir = new File(destDir).getAbsoluteFile().getAbsolutePath();
        while (destDir.contains("..")) {
            String[] sepList = destDir.split("\\");
            destDir = "";
            for (int i = 0; i < sepList.length; i++) {
                if (!"..".equals(sepList[i]) && i < sepList.length - 1 && "..".equals(sepList[i + 1])) {
                    i++;
                } else {
                    destDir += sepList[i] + File.separator;
                }
            }
        }
        boolean bool = false;
        if (!FileUtil.exist(zipFile)) {
            return false;
        }
        if (!FileUtil.exist(destDir)) {
            FileUtil.mkdir(destDir);
        }
        //判断系统类型 开始调用命令行解压,参数-o+是表示覆盖的意思
        String cmdPath = "";
        String osName = System.getProperty("os.name");
        if (osName.contains("Window")) {
            cmdPath = "C:\Program Files\WinRAR\WinRAR.exe";
        } else {
            cmdPath = "/usr/local/bin/unrar";
        }
        String cmd = cmdPath + " X -o+ " + zipFile + " " + destDir;
        System.out.println(cmd);
        try {
            File file = new File(zipFile);
            String s;
            Process proc = Runtime.getRuntime().exec(cmd);
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(proc.getInputStream()));
            while((s = bufferedReader.readLine()) != null) {
                // 输出解压日志
                log.info(file.getName() + "-->" + s);
            }
            if (proc.waitFor() != 0) {
                if (proc.exitValue() == 0) {
                    bool = false;
                }
            } else {
                bool = true;
            }
            // 关闭进程
            proc.destroy();
        } catch (Exception e) {
           log.error("解压失败:" + e.getMessage(), e);
            e.printStackTrace();
        }
        System.out.println("解压" + (bool ? "成功" : "失败"));
        return bool;
    }
}

问题解决收工,记录问题希望能帮到你们linux内存管理,如有错误请强调,感谢

Author

这篇优质的内容由TA贡献而来

刘遄

《Linux就该这么学》书籍作者,RHCA认证架构师,教育学(计算机专业硕士)。

发表回复