java不解压压缩包的前提下,解析压缩包内容

近期工作过程中,遇到了一个需要解析压缩包的功能开发。对于这个功能,如果频繁的解压压缩包,在性能上多少有些损耗,且解压文件,会产生大量中间文件,不适合后续管理。特别是现在都是基于容器化部署,如果文件操作处理不当,导致删除失败,将很快打满容器磁盘容量,引起容器崩溃重启。

综合考虑之后,采用ArchiveInputStream,在不解压文件的前提下,解析压缩包。
具体实践如下。

整体流程
  1. 从SFTP获取压缩包
  2. 读取压缩包文件内容
  3. 删除本地压缩包文件
从SFTP获取压缩包文件

添加项目依赖,使用jcraft包来连接SFTP服务器。

<dependency>
  <groupId>com.jcraft</groupId>
    <artifactId>jsch</artifactId>
    <version>0.1.54</version>
</dependency>

引入依赖后,需要实现一个工具类来连接SFTP。此处只列出关键代码,如果详细工具类,可以网上找一下,有很多。

public class SFTPUtil {

    /**
     * 通过SFTP连接服务器
     */
    public void connect() {
        try {
            JSch jsch = new JSch();
            jsch.getSession(username, host, port);
            sshSession = jsch.getSession(username, host, port);
            if (log.isInfoEnabled()) {
                log.info("Session created.");
            }
            sshSession.setPassword(password);
            Properties sshConfig = new Properties();
            sshConfig.put("StrictHostKeyChecking", "no");
            sshSession.setConfig(sshConfig);
            sshSession.connect();
            if (log.isInfoEnabled()) {
                log.info("Session connected.");
            }
            Channel channel = sshSession.openChannel("sftp");
            channel.connect();
            if (log.isInfoEnabled()) {
                log.info("Opening Channel.");
            }
            sftp = (ChannelSftp) channel;
            if (log.isInfoEnabled()) {
                log.info("Connected to " + host + ".");
            }
        }
        catch (Exception e) {
            log.error("Connected to " + host + "failed.", e);
        }
    }

    /**
     * 关闭连接
     */
    public void disconnect() {
        if (this.sftp != null) {
            if (this.sftp.isConnected()) {
                this.sftp.disconnect();
                if (log.isInfoEnabled()) {
                    log.info("sftp is closed already");
                }
            }
        }
        if (this.sshSession != null) {
            if (this.sshSession.isConnected()) {
                this.sshSession.disconnect();
                if (log.isInfoEnabled()) {
                    log.info("sshSession is closed already");
                }
            }
        }
    }

	/**
     * 下载单个文件
     *
     * @param remotePath:远程下载目录(以路径符号结束)
     * @param remoteFileName:下载文件名
     * @param localPath:本地保存目录(以路径符号结束)
     * @param localFileName:保存文件名
     * @return
     */
    public boolean downloadFile(String remotePath, String remoteFileName, String localPath, String localFileName) {
        FileOutputStream fieloutput = null;
        if (!remotePath.endsWith("/")) {
            remotePath += "/";
        }
        if (!localPath.endsWith("/")) {
            localPath += "/";
        }
        String remoteFilePath = remotePath + remoteFileName;
        try {
            String filePath = localPath + localFileName;
            SftpATTRS attrs = sftp.lstat(remoteFilePath);
            if (attrs.isFifo()){

            }
            // sftp.cd(remotePath);
            mkdirs(localPath);
            File file = new File(filePath);
            fieloutput = new FileOutputStream(file);
            sftp.get(remoteFilePath, fieloutput);
            if (log.isInfoEnabled()) {
                log.info("===DownloadFile:" + remoteFileName + " success from sftp.");
            }
            return true;
        } catch (FileNotFoundException e) {
            log.warn("file not exists ,file name is " + remoteFilePath);
        } catch ( SftpException | BaseAppException e){
            log.error("download file failed.file name is " + remoteFilePath, e);
        } finally {
            if (null != fieloutput) {
                try {
                    fieloutput.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return false;
    }
}
从压缩包中获取文件

核心的代码就是下面这段

public final class TarFileUtil {
	/**
	* 通过文件在压缩包中的路径和文件名称来模糊匹配文件,
	* 并返回第一个匹配上的文件
	*/
	public static byte[] getBytesFromTarFile(File tarFile, String targetFilePath, String targetFileName) {
        ArchiveInputStream archiveInputStream = null;
        try {
            archiveInputStream = getArchiveInputStream(tarFile);
            TarArchiveEntry entry = null;
            while ((entry = (TarArchiveEntry) archiveInputStream.getNextEntry()) != null) {
                if (entry.getSize() <= 0) {
                    continue;
                }
                if (!StringUtils.isEmpty(targetFilePath) && !entry.getName().startsWith(targetFilePath)) {
                    continue;
                }
                if (!StringUtils.isEmpty(targetFileName) && !entry.getName().endsWith(targetFileName)) {
                    continue;
                }
                return FileUtil.getContent(archiveInputStream);
            }

        } catch (Exception e) {
            LOGGER.error("获取压缩包文件失败!", e);
        } finally {
            if (null != archiveInputStream) {
                try {
                    archiveInputStream.close();
                } catch (IOException e) {
                    LOGGER.error("file close error!", e);
                }
            }
        }

        return null;
    }
    private static ArchiveInputStream getArchiveInputStream(File tarFile) throws IOException, ArchiveException {
        if (StringUtils.endsWithIgnoreCase(tarFile.getName(), ".gz")) {
            return new ArchiveStreamFactory()
                    .createArchiveInputStream("tar", new GZIPInputStream(new BufferedInputStream(new FileInputStream(tarFile))));
        } else {
            return new ArchiveStreamFactory()
                    .createArchiveInputStream("tar", new BufferedInputStream(new FileInputStream(tarFile)));
        }
    }
}

通过个方法还可以做一些文件操作的其他方法实现。比如我在工具类中加入了

// 将目标文件的内容转化为String,并返回
public static String readTarFileToStr(File tarFile, String targetFilePath, String targetFileName);

// 将目标文件内容读取出来,写入到本地文件,并返回
public static File readTarFileToFile(File tarFile, String targetFilePath, String targetFileName);

// 列举指定目录下,匹配上文件名的所有文件列表
public static List<String> listFilesInPath(File tarFile, String targetFilePath, String targetFileName);

// 将文件按行读出,并返回内容列表
public static List<String> getLinesFromTarFile(File tarFile, String targetFilePath, String targetFileName);

你也可以根据自己的需要,新增其他方法。因为这些方法实现逻辑类似,此处不再赘述。

注意:ArchiveInputStream的读取和关闭,应保证在一个方法体里,尽量不要将ArchiveInputStream对象作为结果返回出去,在外层做关闭操作,以免文件流关闭失败,导致临时文件无法删除。

删除临时文件

压缩包解析完成后,切记删除下载的压缩包。删除前,确认所有的文件流都关闭了。

public class ResolveHandel{

	public void resolveTarFileMethod() {
		
		try{
			sftpUtil.connect();
			sftpUtil.downloadFile(filePath + fileName);
			
			// do something to resolve package
			
		} catch (Exception e){
			// deal with exception
		} finally {
			// 删除本地缓存文件
            sftpUtil.deleteFile(filePath + fileName);

            // 断开连接
            sftpUtil.disconnect();
		}
	}

}

此处指列出了关键代码,如有不明白的地方,可以留言交流。

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值