JAVA——把一批压缩文件中存放的部分数据进行处理(替换)

news2024/12/23 19:02:14

JAVA——把一批压缩文件中存放的部分数据进行处理(替换)

  • 一、需求
  • 二、分析
  • 三、具体实现
    • 1.解压压缩文件
    • 2.读取解压后的文件并且按照一定逻辑处理
    • 3.把文件压缩存储
    • 4.方法的调用
    • 5.需要添加的依赖
  • 四、执行结果
  • 五、用到的工具类
  • 六、可以改进的地方
    • 1.文件处理完成后,删除临时文件和文件夹会出现删除失败的问题
    • 2.调用方法时所有操作(解压-修改数据-压缩-保存)可以分文件夹完成

一、需求

有一批已经压缩存档的数据文件(压缩文件格式为.tar.gz,解压后为.txt文件),现在需要把每个文件中符合需要的字段数据进行更改替换,因为文件量大,需要替换的字段数据有明确共同点(数据在文件每一行中所处同一个位置),因此写个脚本来处理是比较省时的做法。

  • 样例数据如图所示:
  • 比如说,这个表里存的是用户信息,其中用户名是敏感数据,需要把所有用户名替换成******
    在这里插入图片描述
    在这里插入图片描述

二、分析

以上需求可以分解为三个步骤:
1.解压压缩文件
2.读取解压后的文件并且按照一定逻辑处理
3.把文件压缩存储

三、具体实现

1.解压压缩文件

  • 解压文件方法如下:
/**
     * 批量解压文件夹下的所有.tar.gz文件
     *
     * @param sourcePath 要解压的文件夹
     * @param targetPath 解压到目标路径
     * @return
     */
    public static DataEntity<Void> batchDecompressTarGzip(String sourcePath, String targetPath) throws IOException {
        log.info("{} 开始批量解压文件 ...", sourcePath);
        deleteDirAfterNSeconds(new File(targetPath),0);
        File dir = new File(sourcePath);
        //遍历文件夹,获取文件完整路径
        File[] files = dir.listFiles();
        assert files != null;
        for (File f : files) {
            String filePath = sourcePath + File.separator + f.getName();
            //把所有文件解压到目标文件夹
            deCompressTarGzip(filePath, targetPath + File.separator + dir.getName());
        }

        log.info("{} 文件批量解压完成。", sourcePath);
        return DataUtils.success();
    }
/**
     * 解压.tar.gz文件
     *
     * @param sourcePath 要解压的文件路径加文件名
     * @param targetPath 解压到目标路径
     * @return
     * @throws IOException
     */
    public static DataEntity<Void> deCompressTarGzip(String sourcePath, String targetPath) throws IOException {
//        //解压文件
//        Path source = Paths.get("/home/test/output" + TARGZ);
//        //解压到哪
//        Path target = Paths.get("/home/test2");

        //解压文件
        Path source = Paths.get(sourcePath);
        //解压到哪
        Path target = Paths.get(targetPath);

        if (Files.notExists(source)) {
            return DataUtils.fail("您要解压的文件不存在: " + source.toAbsolutePath());
        }

        //InputStream输入流,以下四个流将tar.gz读取到内存并操作
        //BufferedInputStream缓冲输入流
        //GzipCompressorInputStream解压输入流
        //TarArchiveInputStream解tar包输入流
        try (InputStream fi = Files.newInputStream(source);
             BufferedInputStream bi = new BufferedInputStream(fi);
             GzipCompressorInputStream gzi = new GzipCompressorInputStream(bi);
             TarArchiveInputStream ti = new TarArchiveInputStream(gzi)) {

            ArchiveEntry entry;
            while ((entry = ti.getNextEntry()) != null) {

                //获取解压文件目录,并判断文件是否损坏
                DataEntity<Path> res = zipSlipProtect(entry, target);
                if (!res.isOk()) {
                    return DataUtils.fail(res.getMessage());
                }
                Path newPath = res.getData();
                if (entry.isDirectory()) {
                    //创建解压文件目录
                    Files.createDirectories(newPath);
                } else {
                    //再次校验解压文件目录是否存在
                    Path parent = newPath.getParent();
                    if (parent != null) {
                        if (Files.notExists(parent)) {
                            Files.createDirectories(parent);
                        }
                    }
                    // 将解压文件输入到TarArchiveInputStream,输出到磁盘newPath目录
                    Files.copy(ti, newPath, StandardCopyOption.REPLACE_EXISTING);

                }
            }
        }
        return DataUtils.success();
    }

	//判断压缩文件是否被损坏,并返回该文件的解压目录
    private static DataEntity<Path> zipSlipProtect(ArchiveEntry entry, Path targetDir) {

        Path targetDirResolved = targetDir.resolve(entry.getName());
        Path normalizePath = targetDirResolved.normalize();

        if (!normalizePath.startsWith(targetDir)) {
            log.error("压缩文件已被损坏: {}", entry.getName());
            return DataUtils.fail("压缩文件已被损坏" + entry.getName());
        }
        return DataUtils.entityData(normalizePath);
    }

2.读取解压后的文件并且按照一定逻辑处理

  • 读取和替换文件如下:
/**
     * 批量读取和替换txt文件:把经纬度数据替换成0
     *
     * @param sourcePath 存放.txt文件的目录
     * @return
     */
    public static DataEntity<Void> readAndReplaceTxt(String sourcePath, String targetPath) throws IOException {
        log.info("开始批量替换敏感数据");
        deleteDirAfterNSeconds(new File(targetPath),0);
        File dir = new File(sourcePath);
        File fileDir = dir.listFiles()[0];
        File[] files = fileDir.listFiles();
        //遍历文件
        assert files != null;
        for (File file : files) {
            String newFileName = file.getName();
            String newFileDir = targetPath + File.separator + fileDir.getName();
            Files.createDirectories(Paths.get(newFileDir));
            String newFilePath = newFileDir + File.separator + newFileName;
            try (BufferedReader bufferedReader = new BufferedReader(new FileReader(file))) {
                //构造一个BufferedReader类来读取文件
                String lineTxt = null;
                //result用来存储文件内容
//                    StringBuilder result = new StringBuilder();
                //按使用readLine方法,一次读一行
                while ((lineTxt = bufferedReader.readLine()) != null) {
                    //替换
                    int i = StringUtils.ordinalIndexOf(lineTxt, "\t", 2);
                    int j = StringUtils.ordinalIndexOf(lineTxt, "\t", 4);
                    String newContent = lineTxt.replace(lineTxt.substring(i, j), "\t0\t0");//替换
//                        result.append(newContent);
                    File newFile = new File(newFilePath);
                    PrintStream ps = new PrintStream(new FileOutputStream(newFile, true));
                    ps.println(newContent);// 往.txt文件里写入字符串
                }
                log.info("文件 {} 中敏感数据替换执行完毕", newFilePath);
            } catch (Exception e) {
                log.error("读取文件 {} 内容出错", newFilePath);
                return DataUtils.fail(e.getMessage());
            }
        }

        log.info("敏感数据批量替换完成。");
        return DataUtils.success();
    }

3.把文件压缩存储

  • 压缩文件如下
/**
     * 压缩.txt文件为.tar.gz文件(压缩后文件名不变,仅后缀发生变化)
     *
     * @param resourceFile 要压缩的文件
     * @param targetDir    要存放的路径
     * @return
     * @throws IOException
     */
    public static DataEntity<Void> fileTarGzip(File resourceFile, String targetDir) throws IOException {
        //获取输出后的文件名
        String oldName = resourceFile.getName();
        int i = oldName.indexOf(TXT);
        String newName = oldName.substring(0, i) + TARGZ;
        String outPath = targetDir + File.separator + newName;

        Path path = Paths.get(outPath + TMP);
        try (FileOutputStream fileOutputStream = new FileOutputStream(outPath + TMP);
             BufferedOutputStream bufferedOutput = new BufferedOutputStream(fileOutputStream);
             TarOutputStream tarOutputStream = new TarOutputStream(bufferedOutput);
             FileInputStream fileInputStream = new FileInputStream(resourceFile);
             BufferedInputStream bufferedInput = new BufferedInputStream(fileInputStream);
        ) {
            TarEntry entry = new TarEntry(new File(oldName));
            entry.setSize(resourceFile.length());
            tarOutputStream.putNextEntry(entry);
            IOUtils.copy(bufferedInput, tarOutputStream);
            tarOutputStream.closeEntry();
        } catch (Exception e) {
            Files.delete(path);
            log.error("文件压缩至 {} 执行异常,嵌套异常!", outPath, e);
        }

        // 读取打包好的 tar 临时文件,使用 GZIP 方式压缩
        try (FileInputStream fin = new FileInputStream(outPath + TMP);
             BufferedInputStream bin = new BufferedInputStream(fin);
             FileOutputStream fout = new FileOutputStream(outPath);
             GZIPOutputStream gout = new GZIPOutputStream(fout);
             BufferedOutputStream bout = new BufferedOutputStream(gout);
        ) {
            byte[] cache = new byte[1024];
            for (int index = bin.read(cache); index != -1; index = bin.read(cache)) {
                bout.write(cache, 0, index);
            }
            log.info("文件 {} 压缩执行完毕", outPath);
        } catch (Exception e) {
            log.error("文件压缩至 {} 执行异常,嵌套异常!", outPath, e);
        } finally {
            Files.delete(path);
        }
        return DataUtils.success();
    }

    /**
     * 批量压缩.txt文件为.tar.gz文件(压缩后文件名不变,仅后缀发生变化)
     *
     * @param sourceDir 要压缩的文件存放的目录
     * @param targetDir 要存放的路径
     * @return
     * @throws IOException
     */
    public static DataEntity<Void> filesTarGzip(String sourceDir, String targetDir) throws IOException {
        log.info("{} 开始批量执行压缩文件...", targetDir);
        File dir = new File(sourceDir);
        //获取目标文件夹下的所有文件夹
        File fileDir = dir.listFiles()[0];
        File[] files = fileDir.listFiles();
        assert files != null;
        for (File f : files) {
            fileTarGzip(f, targetDir);
        }

        log.info("{} 文件批量压缩完成。", targetDir);
        return DataUtils.success();
    }

4.方法的调用

FileOperateService.java

@Slf4j
@Service
public class FileOperateService {


    /**
     * 批量处理文件:把压缩文件解压、将解压后的文件经纬度数据替换成0,重新把文件压缩回原来的目录
     * @param source    文件夹路径:存放要处理的文件
     * @param temp      文件夹路径:任意临时文件夹
     * @param temp2     文件夹路径:任意临时文件夹
     * @return
     * @throws IOException
     */
    public DataEntity<Void> operate(String source, String temp, String temp2) throws IOException {
        //把source目录下的.tar.gz文件批量解压成.txt文件到临时temp目录下
        DataEntity<Void> decompressFiles = FileUpdateUtils.batchDecompressTarGzip(source, temp);
        if (decompressFiles.isOk()) {
            //把临时temp目录下的.txt文件批量替换掉经纬度数据后,存到临时temp2目录下
            DataEntity<File[]> readAndReplaceData = FileUpdateUtils.readAndReplaceTxt(temp, temp2);
            //如果经纬度替换成功,就删除临时目录temp
            FileUpdateUtils.deleteTempDir(temp);
            if (readAndReplaceData.isOk()) {
                //把临时temp2目录下的.txt文件压缩成.tar.gz文件,存放到source目录下
                DataEntity<Void> compressFiles = FileUpdateUtils.filesTarGzip(temp2, source);
                //如果压缩成功,就删除临时目录temp2
                FileUpdateUtils.deleteTempDir(temp2);
                return compressFiles;
            } else {
                return DataUtils.fail(readAndReplaceData.getMessage());
            }
        }
        return decompressFiles;
    }
    

}

FileUpdateResource.java

@RestController
@RequestMapping("/api")
public class FileUpdateResource {

    private final Logger log = LoggerFactory.getLogger(FileUpdateResource.class);

    private final FileOperateService fileOperateService;


    public FileUpdateResource(FileOperateService fileOperateService) {
        this.fileOperateService = fileOperateService;
    }

    /**
     * 批量处理文件:把压缩文件内的经纬度数据替换成0
     * @param source    文件夹路径:存放要处理的文件
     * @param temp      文件夹路径:任意临时文件夹
     * @param temp2     文件夹路径:任意临时文件夹
     * @return
     * @throws IOException
     */
    @PostMapping("/file/operate")
    public DataEntity<Void> operateFile(@RequestParam("source") String source,@RequestParam("temp") String temp,@RequestParam("temp2") String temp2) throws IOException {
        log.debug("REST request operate File");
        return fileOperateService.operate(source,temp,temp2);
    }
}

5.需要添加的依赖

        <dependency>
            <groupId>org.apache.ant</groupId>
            <artifactId>ant</artifactId>
            <version>1.8.1</version>
        </dependency>
        <dependency>
            <groupId>com.github.junrar</groupId>
            <artifactId>junrar</artifactId>
            <version>0.7</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </dependency>

四、执行结果

如图所示:

在这里插入图片描述

五、用到的工具类

import org.apache.commons.compress.archivers.ArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream;
import org.apache.commons.compress.utils.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.tools.tar.TarEntry;
import org.apache.tools.tar.TarOutputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.zip.GZIPOutputStream;

public class FileUpdateUtils {
    public static final String TARGZ = ".tar.gz";
    private static final String TXT = ".txt";
    private static final String TMP = ".tmp";
    private static final Logger log = LoggerFactory.getLogger(TarGzUtils.class);

    // 文件删除任务的线程池
    private static ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);

    /**
     * 批量解压文件夹下的所有.tar.gz文件
     *
     * @param sourcePath 要解压的文件夹
     * @param targetPath 解压到目标路径
     * @return
     */
    public static DataEntity<Void> batchDecompressTarGzip(String sourcePath, String targetPath) throws IOException {
        log.info("{} 开始批量解压文件 ...", sourcePath);
        deleteDirAfterNSeconds(new File(targetPath),0);
        File dir = new File(sourcePath);
        //遍历文件夹,获取文件完整路径
        File[] files = dir.listFiles();
        assert files != null;
        for (File f : files) {
            String filePath = sourcePath + File.separator + f.getName();
            //把所有文件解压到目标文件夹
            deCompressTarGzip(filePath, targetPath + File.separator + dir.getName());
        }

        log.info("{} 文件批量解压完成。", sourcePath);
        return DataUtils.success();
    }


    /**
     * 解压.tar.gz文件
     *
     * @param sourcePath 要解压的文件路径加文件名
     * @param targetPath 解压到目标路径
     * @return
     * @throws IOException
     */
    public static DataEntity<Void> deCompressTarGzip(String sourcePath, String targetPath) throws IOException {
//        //解压文件
//        Path source = Paths.get("/home/test/output" + TARGZ);
//        //解压到哪
//        Path target = Paths.get("/home/test2");

        //解压文件
        Path source = Paths.get(sourcePath);
        //解压到哪
        Path target = Paths.get(targetPath);

        if (Files.notExists(source)) {
            return DataUtils.fail("您要解压的文件不存在: " + source.toAbsolutePath());
        }

        //InputStream输入流,以下四个流将tar.gz读取到内存并操作
        //BufferedInputStream缓冲输入流
        //GzipCompressorInputStream解压输入流
        //TarArchiveInputStream解tar包输入流
        try (InputStream fi = Files.newInputStream(source);
             BufferedInputStream bi = new BufferedInputStream(fi);
             GzipCompressorInputStream gzi = new GzipCompressorInputStream(bi);
             TarArchiveInputStream ti = new TarArchiveInputStream(gzi)) {

            ArchiveEntry entry;
            while ((entry = ti.getNextEntry()) != null) {

                //获取解压文件目录,并判断文件是否损坏
                DataEntity<Path> res = zipSlipProtect(entry, target);
                if (!res.isOk()) {
                    return DataUtils.fail(res.getMessage());
                }
                Path newPath = res.getData();
                if (entry.isDirectory()) {
                    //创建解压文件目录
                    Files.createDirectories(newPath);
                } else {
                    //再次校验解压文件目录是否存在
                    Path parent = newPath.getParent();
                    if (parent != null) {
                        if (Files.notExists(parent)) {
                            Files.createDirectories(parent);
                        }
                    }
                    // 将解压文件输入到TarArchiveInputStream,输出到磁盘newPath目录
                    Files.copy(ti, newPath, StandardCopyOption.REPLACE_EXISTING);

                }
            }
        }
        return DataUtils.success();
    }


    /**
     * 批量读取和替换txt文件:把经纬度数据替换成0
     *
     * @param sourcePath 存放.txt文件的目录
     * @return
     */
    public static DataEntity<Void> readAndReplaceTxt(String sourcePath, String targetPath) throws IOException {
        log.info("开始批量替换敏感数据");
        deleteDirAfterNSeconds(new File(targetPath),0);
        File dir = new File(sourcePath);
        File fileDir = dir.listFiles()[0];
        File[] files = fileDir.listFiles();
        //遍历文件
        assert files != null;
        for (File file : files) {
            String newFileName = file.getName();
            String newFileDir = targetPath + File.separator + fileDir.getName();
            Files.createDirectories(Paths.get(newFileDir));
            String newFilePath = newFileDir + File.separator + newFileName;
            try (BufferedReader bufferedReader = new BufferedReader(new FileReader(file))) {
                //构造一个BufferedReader类来读取文件
                String lineTxt = null;
                //result用来存储文件内容
//                    StringBuilder result = new StringBuilder();
                //按使用readLine方法,一次读一行
                while ((lineTxt = bufferedReader.readLine()) != null) {
                    //替换
                    int i = StringUtils.ordinalIndexOf(lineTxt, "\t", 2);
                    int j = StringUtils.ordinalIndexOf(lineTxt, "\t", 4);
                    String newContent = lineTxt.replace(lineTxt.substring(i, j), "\t0\t0");//替换
//                        result.append(newContent);
                    File newFile = new File(newFilePath);
                    PrintStream ps = new PrintStream(new FileOutputStream(newFile, true));
                    ps.println(newContent);// 往.txt文件里写入字符串
                }
                log.info("文件 {} 中敏感数据替换执行完毕", newFilePath);
            } catch (Exception e) {
                log.error("读取文件 {} 内容出错", newFilePath);
                return DataUtils.fail(e.getMessage());
            }
        }

        log.info("敏感数据批量替换完成。");
        return DataUtils.success();
    }

    //判断压缩文件是否被损坏,并返回该文件的解压目录
    private static DataEntity<Path> zipSlipProtect(ArchiveEntry entry, Path targetDir) {

        Path targetDirResolved = targetDir.resolve(entry.getName());
        Path normalizePath = targetDirResolved.normalize();

        if (!normalizePath.startsWith(targetDir)) {
            log.error("压缩文件已被损坏: {}", entry.getName());
            return DataUtils.fail("压缩文件已被损坏" + entry.getName());
        }
        return DataUtils.entityData(normalizePath);
    }

    /**
     * 压缩.txt文件为.tar.gz文件(压缩后文件名不变,仅后缀发生变化)
     *
     * @param resourceFile 要压缩的文件
     * @param targetDir    要存放的路径
     * @return
     * @throws IOException
     */
    public static DataEntity<Void> fileTarGzip(File resourceFile, String targetDir) throws IOException {
        //获取输出后的文件名
        String oldName = resourceFile.getName();
        int i = oldName.indexOf(TXT);
        String newName = oldName.substring(0, i) + TARGZ;
        String outPath = targetDir + File.separator + newName;

        Path path = Paths.get(outPath + TMP);
        try (FileOutputStream fileOutputStream = new FileOutputStream(outPath + TMP);
             BufferedOutputStream bufferedOutput = new BufferedOutputStream(fileOutputStream);
             TarOutputStream tarOutputStream = new TarOutputStream(bufferedOutput);
             FileInputStream fileInputStream = new FileInputStream(resourceFile);
             BufferedInputStream bufferedInput = new BufferedInputStream(fileInputStream);
        ) {
            TarEntry entry = new TarEntry(new File(oldName));
            entry.setSize(resourceFile.length());
            tarOutputStream.putNextEntry(entry);
            IOUtils.copy(bufferedInput, tarOutputStream);
            tarOutputStream.closeEntry();
        } catch (Exception e) {
            Files.delete(path);
            log.error("文件压缩至 {} 执行异常,嵌套异常!", outPath, e);
        }

        // 读取打包好的 tar 临时文件,使用 GZIP 方式压缩
        try (FileInputStream fin = new FileInputStream(outPath + TMP);
             BufferedInputStream bin = new BufferedInputStream(fin);
             FileOutputStream fout = new FileOutputStream(outPath);
             GZIPOutputStream gout = new GZIPOutputStream(fout);
             BufferedOutputStream bout = new BufferedOutputStream(gout);
        ) {
            byte[] cache = new byte[1024];
            for (int index = bin.read(cache); index != -1; index = bin.read(cache)) {
                bout.write(cache, 0, index);
            }
            log.info("文件 {} 压缩执行完毕", outPath);
        } catch (Exception e) {
            log.error("文件压缩至 {} 执行异常,嵌套异常!", outPath, e);
        } finally {
            Files.delete(path);
        }
        return DataUtils.success();
    }

    /**
     * 批量压缩.txt文件为.tar.gz文件(压缩后文件名不变,仅后缀发生变化)
     *
     * @param sourceDir 要压缩的文件存放的目录
     * @param targetDir 要存放的路径
     * @return
     * @throws IOException
     */
    public static DataEntity<Void> filesTarGzip(String sourceDir, String targetDir) throws IOException {
        log.info("{} 开始批量执行压缩文件...", targetDir);
        File dir = new File(sourceDir);
        //获取目标文件夹下的所有文件夹
        File fileDir = dir.listFiles()[0];
        File[] files = fileDir.listFiles();
        assert files != null;
        for (File f : files) {
            fileTarGzip(f, targetDir);
        }

        log.info("{} 文件批量压缩完成。", targetDir);
        return DataUtils.success();
    }

    /**
     * 删除一个文件夹和里面的文件
     *
     * @param dirPath 文件夹路径
     * @return
     */
    public static DataEntity<Void> deleteTempDir(String dirPath) {
        System.gc();
        File file = new File(dirPath);
        deleteFile(file);
//        if (deleteFile(file).isOk()) {
//            log.info(dirPath + "目录下文件删除成功!");
//        }
        return DataUtils.success();
    }

        public static DataEntity<Void> deleteFile(File file) {
        //判断文件不为null或文件目录存在
        if (file == null || !file.exists()) {
            return DataUtils.fail("文件删除失败,请检查文件是否存在以及文件路径是否正确");
        }
        //获取目录下子文件
        File[] files = file.listFiles();
        //遍历该目录下的文件对象
        assert files != null;
        for (File f : files) {
            //判断子目录是否存在子目录,如果是文件则删除
            if (f.isDirectory()) {
                //递归删除目录下的文件
                deleteFile(f);
            } else {
                //文件删除
                f.delete();
            }
        }
        //文件夹删除
        file.delete();
        return DataUtils.success();
    }

    /**
     * 延迟若干秒后执行删除操作
     * @param file
     * @param seconds
     */
    public static void deleteDirAfterNSeconds(File file, int seconds) {
        scheduledThreadPool.schedule(new Runnable() {
            public void run() {
                try {
                    if (file.exists()) {
                        // 两种删除方式都用上
                        deleteDir(file);
                        deleteDirByCmd(file);
                    }
                } catch (Throwable e) {
                    e.printStackTrace();
                }
            }
        }, seconds, TimeUnit.SECONDS);
    }

    /**
     * 通过调用系统命令删除一个文件夹及下面的所有文件
     */
    public static void deleteDirByCmd(File file) {
        // 文件不存在直接返回
        if (null == file || !file.exists()) {
            return;
        }

        // 文件存在时执行删除操作
        Runtime rt = Runtime.getRuntime();
        try {
            String cmd = null;
            if (file.isFile()) {
                cmd = "cmd.exe /c del /q/a/f/s " + file.getAbsolutePath();
            } else {
                cmd = "cmd.exe /c rd /s/q " + file.getAbsolutePath();
            }
            rt.exec(cmd);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 递归删除目录下的所有文件及子目录下所有文件
     * @param dir 将要删除的文件目录|文件
     */
    public static boolean deleteDir(File dir) {
        if (dir.isDirectory()) {
            String[] children = dir.list();
            for (int i = 0; i < children.length; i++) {
                boolean success = deleteDir(new File(dir, children[i]));
                if (!success) {
                    return false;
                }
            }
        }
        return dir.delete();
    }




}

六、可以改进的地方

1.文件处理完成后,删除临时文件和文件夹会出现删除失败的问题

问题背景:

通过IO流操作过文件后,最后想删除过程中产生的临时文件,有部分文件未被删除,也不报错。
已经检查过所有的流都关闭了。

问题原因:

临时文件对象在java虚拟机(jvm)中还未被解除引用,虽然看似没有变量指向这个临时文件对象了,但还没来得急被垃圾收集器自动回收解除引用。

解决:

方案一:执行删除操作前手动把垃圾回收一下

/**
 * 删除一个文件夹和里面的文件
 *
 * @param dirPath 文件夹路径
 * @return
 */
public static DataEntity<Void> deleteTempDir(String dirPath) {
    System.gc();
    File file = new File(dirPath);
    deleteFile(file);
    if (deleteFile(file).isOk()) {
        log.info(dirPath + "目录下文件删除成功!");
    }
    return DataUtils.success();
}

public static DataEntity<Void> deleteFile(File file) {
    //判断文件不为null或文件目录存在
    if (file == null || !file.exists()) {
        return DataUtils.fail("文件删除失败,请检查文件是否存在以及文件路径是否正确");
    }
    //获取目录下子文件
    File[] files = file.listFiles();
    //遍历该目录下的文件对象
    assert files != null;
    for (File f : files) {
        //判断子目录是否存在子目录,如果是文件则删除
        if (f.isDirectory()) {
            //递归删除目录下的文件
            deleteFile(f);
        } else {
            //文件删除
            f.delete();
        }
    }
    //文件夹删除
    file.delete();
    return DataUtils.success();
}

方案二:延时若干秒后对文件进行删除

import java.io.File;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * 文件删除工具类
 */
public class FileDeleteUtil {

    // 文件删除任务的线程池
    private static ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);

    /**
     * 延迟若干秒后执行删除操作
     * @param file
     * @param seconds
     */
    public static void deleteDirAfterNSeconds(File file, int seconds) {
        scheduledThreadPool.schedule(new Runnable() {
            public void run() {
                try {
                    if (file.exists()) {
                        // 两种删除方式都用上
                        deleteDir(file);
                        deleteDirByCmd(file);
                    }
                } catch (Throwable e) {
                    e.printStackTrace();
                }
            }
        }, seconds, TimeUnit.SECONDS);
    }

    /**
     * 通过调用系统命令删除一个文件夹及下面的所有文件
     */
    public static void deleteDirByCmd(File file) {
        // 文件不存在直接返回
        if (null == file || !file.exists()) {
            return;
        }

        // 文件存在时执行删除操作
        Runtime rt = Runtime.getRuntime();
        try {
            String cmd = null;
            if (file.isFile()) {
                cmd = "cmd.exe /c del /q/a/f/s " + file.getAbsolutePath();
            } else {
                cmd = "cmd.exe /c rd /s/q " + file.getAbsolutePath();
            }
            rt.exec(cmd);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 递归删除目录下的所有文件及子目录下所有文件
     * @param dir 将要删除的文件目录|文件
     */
    public static boolean deleteDir(File dir) {
        if (dir.isDirectory()) {
            String[] children = dir.list();
            for (int i = 0; i < children.length; i++) {
                boolean success = deleteDir(new File(dir, children[i]));
                if (!success) {
                    return false;
                }
            }
        }
        return dir.delete();
    }
}

2.调用方法时所有操作(解压-修改数据-压缩-保存)可以分文件夹完成

全量数据都一次性操作完成的话,在数据量大的情况下,就会非常占用内存空间,可以采用所有操作分目录进行(解压-修改数据-压缩-保存)的方式来,修改调用代码如下:
(同时把方法调用的参数写到了配置文件里,因为本操作只需要执行一次,所以不需要写成接口。)

/**
     * 批量处理文件:把压缩文件解压、将解压后的文件经纬度数据替换成0,重新把文件压缩回原来的目录
     * @return
     * @throws IOException
     */
    public DataEntity<Void> operate() throws IOException {
        String source = conf.getSource();
        String temp = conf.getTemp();
        String temp2 = conf.getTemp2();
        Integer start = conf.getStart();
        Integer end = conf.getEnd();

        File dirs = new File(source);

        for (File dir : dirs.listFiles()) {
            String dirName = dir.getName();
            Integer date = getDate(dirName);
            //遍历日期文件夹,处理日期范围内的文件夹内的压缩文件
            if (start <= date && date <= end) {
                //把source\日期目录下的.tar.gz文件批量解压成.txt文件到临时temp目录下
                DataEntity<Void> decompressFiles = FileUpdateUtils.batchDecompressTarGzip(source+File.separator+dirName, temp);
                if (decompressFiles.isOk()) {
                    //把临时temp目录下的.txt文件批量替换掉经纬度数据后,存到临时temp2目录下
                    DataEntity<Void> readAndReplaceData = FileUpdateUtils.readAndReplaceTxt(temp, temp2);
                    if (readAndReplaceData.isOk()) {
                        //把临时temp2目录下的.txt文件压缩成.tar.gz文件,存放到source目录下
                        DataEntity<Void> compressFiles = FileUpdateUtils.filesTarGzip(temp2, source+File.separator+dirName);
                    }
                }
                //删除临时目录和其中的临时文件
                FileUpdateUtils.deleteTempDir(temp);
                FileUpdateUtils.deleteTempDir(temp2);
                log.info("{} 敏感数据已经完成替换。", date);
            }
        }
        //再次删除临时目录和其中的临时文件
        FileUpdateUtils.deleteDirAfterNSeconds(new File(temp),3);
        FileUpdateUtils.deleteDirAfterNSeconds(new File(temp2),3);

        return DataUtils.success();
    }

    private Integer getDate(String dirName) {
        return Integer.parseInt(dirName.substring(0, 8));
    }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/115778.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

OpenText Exceed TurboX 客户案例——SMS 集团

SMS 集团通过 OpenText 提高工程师的工作效率。OpenText Exceed TurboX 帮助该制造商提高生产力和可靠性、降低成本并实现全球协作。 SMS集团存在的挑战 需要一个可以在全球范围内轻松访问的解决方案&#xff1b;需要一个系统&#xff0c;能够无缝运行图形要求苛刻的基于服务…

Git知识型整理

目的 了解Git&#xff0c; 以及工作原理&#xff0c; 能更好让自己解决在仓库中遇到的各种问题。 git 什么是git 分布式版本控制系统&#xff0c;可以有效、高速地处理从很小到非常大的项目版本管理。很多版本管理工具如&#xff1a;sourcetree, gitLab, tortoise等等&#…

贵金属软件MT4好不好用?MT4软件有什么优势特点?

如今的贵金属市场还是比较大的&#xff0c;有许多投资者都希望可以在里面赚一分钱&#xff0c;其实在市场变得很大的时候&#xff0c;我们更应该小心一点。因为如今市场当中能够选择的软件实在是太多了&#xff0c;我们可以查看一下哪一个软件比较适合投资&#xff0c;在挑选的…

1、初识C语言---“hello world”

文章目录1、什么是C语言呢?2、第一个C语言程序3、数据类型4、变量与常量4.1变量的定义方法4.2变量的命名规则4.3变量的使用4.4变量的作用域与生命周期4.5常量5.字符串转义字符注释5.1字符串5.2转义字符5.3注释1、什么是C语言呢? C语言是一门面向过程的、抽象化的通用程序设计…

Spring Boot学习篇(二)

Spring Boot学习篇(二) 1.spring boot中的拦截器 1.1 在com包.zlz包下面创建interceptor包,包的目录结构如下所示 1.2 在interceptor包下面创建MyInterceptor1和MyInterceptor2类(便于测试顺序) 1.2.1 MyInterceptor1类 package com.zlz.interceptor;import org.springfram…

集合家庭作业

Homework01 按要求实现: (1)封装一个新闻类&#xff0c;包含标题和内容属性&#xff0c;提供get、set方法,重写toString方法&#xff0c;打印对象时只打印标题; (2)只提供一个带参数的构造器&#xff0c;实例化对象时&#xff0c;只初始化标题;并且实例化两个对象: 新闻一:…

【C数组】详解数组

数组前言一、一维数组的创建和初始化&#xff08;一&#xff09;数组的创建1.数组的概念和创建方式2.变长数组&#xff08;二&#xff09;数组的初始化&#xff08;三&#xff09;一维数组的使用&#xff08;四&#xff09;一维数组在内存中的存储二、二维数组的创建和初始化&a…

rabbitmq基础7——队列和消息过期时间设置、死信队列、延迟队列、优先级队列、回调队列、惰性队列

文章目录一、过期时间1.1 针对队列设置1.2 针对消息设置二、死信队列2.1 死信交换器2.2 死信队列原理2.3 延迟队列&#xff08;特殊用法&#xff09;三、优先级队列3.1 监控页面创建优先级队列3.2 监控页面创建优先级消息四、回调队列4.1 RPC的定义4.2 PRC工作机制4.3 监控页面…

java本地socket服务端暴露至公网访问【内网穿透】

Java 服务端demo环境 jdk1.8 框架:springbootmaven 开发工具:IDEA 在pom文件引入第三包封装的netty框架maven坐标 <dependency><groupId>io.github.fzdwx</groupId><artifactId>sky-http-springboot-starter</artifactId><version>0.…

叮叮当~~叮叮当~~|您有一份白玉兰酒店圣诞节豪礼等待抱走!

不知不觉2022年已经临近尾声 全国各地都已经放开了 相信有很多小伙伴在这一年 都没有好好的出来浪一浪 在即将到来的圣诞节 各地的圣诞玩乐都已经齐齐亮相 同时 白玉兰酒店客房 也已迎来了圣诞系列主题 是时候计划一场完美的圣诞之旅了 兰小姐为您准备了丰富多彩的圣…

【苹果家庭群发推送】软件安装Apple推送是一种基于IMESSAGE平台的新信息推送功效的营销软件

推荐内容IMESSGAE相关 作者推荐内容iMessage苹果推软件 *** 点击即可查看作者要求内容信息作者推荐内容1.家庭推内容 *** 点击即可查看作者要求内容信息作者推荐内容2.相册推 *** 点击即可查看作者要求内容信息作者推荐内容3.日历推 *** 点击即可查看作者要求内容信息作者推荐…

DevOps-7:Jenkins API介绍

前言&#xff1a; 为什么要使用Jenkins的API&#xff1f; 我在使用Jenkins的过程中&#xff0c;觉得Jenkins的UI还是有不少问题的&#xff1a; UI性能差&#xff0c;尤其是有一些任务在构建中时&#xff0c;UI非常卡顿&#xff0c;等个十来秒都正常&#xff0c;极端时甚至会崩…

LeetCode 221. 最大正方形

LeetCode 221. 最大正方形 在一个由 0 和 1 组成的二维矩阵内&#xff0c;找到只包含 1 的最大正方形&#xff0c;并返回其面积。 示例 1&#xff1a; 输入&#xff1a;matrix [["1","0","1","0","0"],["1",&quo…

【OpenCV-Python】教程:8-1 图像去噪 Image Denoising

OpenCV Python 图像去噪 Image Denoising 【目标】 非局部均值去噪算法去除图像中的噪声。 cv2.fastNlMeansDenoising() , cv2.fastNlMeansDenoisingColored() etc. 【理论】 在前面的章节中&#xff0c;我们已经看到了许多图像平滑技术&#xff0c;如高斯模糊&#xff0c…

1、MYSQL基础(DDL DML DCL)

&#xff08;1&#xff09;关于字段修改 change和modify的异同&#xff1a; 同&#xff1a;可以修改表的定义 异&#xff1a;change需要写两次列名&#xff0c;不方便&#xff0c;但是change可以修改表名&#xff0c;modify不可 &#xff08;2&#xff09;多表更新&#xff…

店铺如何快速实现数字化管理?不妨参考一下管理系统

百数店铺管理系统主要是以门店管理为核心&#xff0c;该应用管理涵盖商品、订单、库存、客户、采购、财务、营销等功能体系&#xff0c;维度数据分析&#xff0c;智能指导门店经营&#xff0c;账目清晰一目了然&#xff0c;店铺经营更高效。 1、销售看板 该分析报表里通过销售…

手把手教你玩转 Excel 数据透视表

1. 什么是数据透视表 数据透视表是一种可以快速汇总、分析大量数据表格的交互式分析工具。使用数据透视表可以按照数据表格的不同字段从多个角度进行透视&#xff0c;并建立交叉表格&#xff0c;用以查看数据表格不同层面的汇总信息、分析结果以及摘要数据。 使用数据透视表…

【Vue + Koa 前后端分离项目实战6】使用开源框架==>快速搭建后台管理系统 -- part6 前端实现期刊列表管理【增删查改】

要把努力当成一种习惯&#xff0c;而不是一时热血。 对应后端部分章节回顾&#xff1a; 【Vue Koa 前后端分离项目实战4】使用开源框架&#xff1e;快速搭建后台管理系统 -- part4 后端实现【增删改查】功能_小白Rachel的博客-CSDN博客 目录 一、前端项目准备 1.运行项目 …

支持百问网T113 D1-H D1s V853 V851s 等开发板 使用 Tina Linux NOR Flash文件系统 开发指南

此文章内容适用于 百问网T113 D1-H D1s V853 V851s 等开发板&#xff0c;开发板详情请访问 www.100ask.net 。 1 简介 编写目的 此文档描述Sunxi NOR 模块的使用方法&#xff0c;为相关人员调试提供指导 适用范围 boot0: 适用于brandy-2.0u-boot: 适用于u-boot-2018kernel: …

记录内值排序

记录内值排序 【问题】 could anyone please suggest a way to accomplish this. i have a table which consists of six columns : Table name : orders num1 number, num2 number , num3 number , num4 number , num5 number , num6 number there is a routine which fi…