Java操作文件的日常总结(文件压缩,文件解压,递归删除文件,文件的下载保存)

简介: 文件的读写是日常开发中经常碰到的需求,熟练掌握文件的读写是每个Java开发者的基本功。本文将主要介绍常见的文件读写操作。之前写过一篇Java IO的文章 Java IO基础(同步阻塞IO)。

简介

文件的读写是日常开发中经常碰到的需求,熟练掌握文件的读写是每个Java开发者的基本功。本文将主要介绍常见的文件读写操作。

之前写过一篇Java IO的文章 Java IO基础(同步阻塞IO)

1. 文件的下载

文件的下载是指通过文件的url在网络中得到文件的输入流。

这里直接通过java自带的HttpURLConnection来从网络中获取文件流。

public static InputStream downloadByUrl(String urlPath) {
        try {
            URL url = new URL(urlPath);
            HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
            httpURLConnection.setRequestProperty("Charset", "UTF-8");
            httpURLConnection.connect();
            return httpURLConnection.getInputStream();
        } catch (Exception e) {
            return null;
        }
    }

在请求的时候需要设置文件的编码是UTF-8。

2. 将下载到文件保存到本地

/**
     * 下载文件并保存
     *
     * @param fileUrl  文件的url
     * @param fileName 文件的原始文件名
     * TARGET_DIR 是文件的保存目录,可以自行指定
     * @return
     * @Author 码农飞哥
     */
    public static String downloadAndSaveFile(String fileUrl, String fileName) throws IOException {
        if (StringUtils.isAnyBlank(fileUrl, fileName)) {
            return null;
        }
        //2.保存文件
        File file = new File(TARGET_DIR);
        if (!file.exists()) {
            file.mkdirs();
        }
        String[] fileNameStr = fileName.split(FileUtil.DOU_SUFFIX);
        String newFileName = fileNameStr[0] + "." + fileNameStr[1];
        File targetFile = new File(TARGET_DIR + "/" + newFileName);
        InputStream is = null;
        FileOutputStream fos = null;
        //1.下载文件
        try {
            is = downloadByUrl(fileUrl);
            fos = new FileOutputStream(targetFile);
            byte[] bufferByte = new byte[1024];
            int len;
            while ((len = is.read(bufferByte)) > 0) {
                fos.write(bufferByte, 0, len);
            }
            fos.flush();
        } finally {
            IOUtils.closeQuietly(is);
            IOUtils.closeQuietly(fos);
        }
        return targetFile.getAbsolutePath();
    }

这里就是将文件下载下来并保存到本地,通过downloadByUrl 方法得到文件流,然后,定义输出流FileOutputStream 来将数据写入到目标文件中,在操作文件时一定要注意在finally代码块中关闭输入流和输出流。

3. 文件压缩

文件压缩的整体思路是:

1.指定待压缩的目录以及压缩文件名,定义zip文件的输出流zos。

2.如果文件是一个文件则向zos中添加一个zipEntry实体

3.将源目录下的文件数据写入zos中。

4.如果是空文件夹需要特殊处理

5.如果是非空文件夹,需要循环其子目录,递归压缩。

/**
     * @param srcFile   需要压缩的文件目录
     * @param targetDir 压缩文件的目标路径
     * @return
     */
    public static void zipFile(File srcFile, String targetDir) {
        long start = System.currentTimeMillis();
        FileOutputStream out = null;
        ZipOutputStream zos = null;
        try {
            out = new FileOutputStream(new File(targetDir));
            zos = new ZipOutputStream(out);
            compress(srcFile, zos, srcFile.getName(), true);
            long end = System.currentTimeMillis();
            System.out.println("压缩完成,耗时:" + (end - start) + " ms");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (zos != null) {
                try {
                    zos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            IOUtils.closeQuietly(out);
        }
    }
    /**
     * 递归压缩方法
     *
     * @param sourceFile       源文件
     * @param zos              zip输出流
     * @param name             压缩后的名称
     * @param keepDirStructure 是否保留原来的目录结构,true:保留目录结构;
     *                         false:所有文件跑到压缩包根目录下(注意:不保留目录结构可能会出现同名文件,会压缩失败)
     * @throws Exception
     */
    private static void compress(File sourceFile, ZipOutputStream zos, String name, boolean keepDirStructure) throws IOException {
        byte[] buf = new byte[1024];
        if (sourceFile.isFile()) {
            //向zip输出流中添加一个zip实体,构造器name为zip实体文件的名字
            zos.putNextEntry(new ZipEntry(name));
            //copy文件到zip输出流中
            int len;
            FileInputStream in = null;
            try {
                in = new FileInputStream(sourceFile);
                while ((len = in.read(buf)) > 0) {
                    zos.write(buf, 0, len);
                }
                zos.flush();
            } finally {
                zos.closeEntry();
                IOUtils.closeQuietly(in);
            }
        } else {
            File[] listFiles = sourceFile.listFiles();
            if (listFiles == null || listFiles.length == 0) {
                // 判断是否需要保留原来的文件结构时,需要对空文件夹进行处理
                if (keepDirStructure) {
                    //空文件夹的处理
                    zos.putNextEntry(new ZipEntry(name + "/"));
                    // 没有文件,不需要文件的copy
                    zos.closeEntry();
                }
            } else {
                for (File file : listFiles) {
                    // 判断是否需要保留原来的文件结构
                    if (keepDirStructure) {
                        // 注意:file.getName()前面需要带上父文件夹的名字加一斜杠,
                        // 不然最后压缩包中就不能保留原来的文件结构,即:所有文件都跑到压缩包根目录下了
                        compress(file, zos, name + "/" + file.getName(), keepDirStructure);
                    } else {
                        compress(file, zos, file.getName(), keepDirStructure);
                    }
                }
            }
        }
    }

4. 文件解压

通过ZipFile来读取压缩包,整体思路就是:


首先定义好解压之后的目录,一般就是跟zip文件的同名目录。

定义ZipFile对象,不过需要指定编码gbk,不然可能会出现中文乱码的情况

通过 zfile.entries() 获取zipFile对象下的所有元素,通过 zList.hasMoreElements() 是否为true不断循环读取

通过 zList.nextElement() 获取压缩包里zipEntry实体,这个实体可能是文件也可能是目录

通过 newFile(destDir, zipEntry) 方法来创建zipEntry实体的父目录。

如果zipEntry对象是目录的话,则直接返回

如果zipEntry对象是文件的话,则需要将该文件的数据写入到本地。需要注意的是输入流和输出流用完之后需要立马在finally代码块中关闭掉。

//zipFileDir: zip文件的绝对路径
  public static String unzipFile(String zipFileDir) throws IOException {
        //接收解压后的存放路径, 兼容文件名带多个点的情况
        String targetDir = zipFileDir.substring(0, zipFileDir.indexOf(".zip"));
        File destDir = new File(targetDir);
        if (!destDir.exists()) {
            destDir.mkdirs();
        }
        ZipFile zfile = null;
        byte[] buffer = new byte[1024];
        InputStream fis = null;
        ZipEntry zipEntry;
        FileOutputStream fos = null;
        try {
            //指定编码,防止由于中文导致的MALFORMED问题
            zfile = new ZipFile(zipFileDir, Charset.forName("gbk"));
            Enumeration zList = zfile.entries();
            // 遍历压缩包中的所有元素
            while (zList.hasMoreElements()) {
                zipEntry = (ZipEntry) zList.nextElement();
                //创建子目录
                File newFile = newFile(destDir, zipEntry);
                //文件夹的话就继续
                if (zipEntry.isDirectory()) {
                    continue;
                }
                try {
                    fis = new BufferedInputStream(zfile.getInputStream(zipEntry));
                    fos = new FileOutputStream(newFile);
                    int len;
                    while ((len = fis.read(buffer)) > 0) {
                        fos.write(buffer, 0, len);
                    }
                    //放在循环外效率更高
                    fos.flush();
                } finally {
                    IOUtils.closeQuietly(fos);
                    IOUtils.closeQuietly(fis);
                }
            }
        } finally {
            if (zfile != null) {
                try {
                    zfile.close();
                } catch (IOException e) {
                }
            }
        }
        return targetDir;
    }
    //创建子目录
   private static File newFile(File destinationDir, ZipEntry zipEntry) {
        File destFile = new File(destinationDir, zipEntry.getName());
        String destFilePath = destFile.getParent();
        File file = new File(destFilePath);
        if (!file.exists()) {
            file.mkdirs();
        }
        return destFile;
    }

5. 递归删除文件

本地文件或者文件夹使用完成之后,如果不需要的话,则需要删除掉,java不能直接删除一个非空的目录,只能通过递归的方式循环删除目录下的所有文件才能删除外层的目录。

//targetFile 需要递归删除的目录
  public static void deleteFile(File targetFile) {
        if (!targetFile.exists()) {
            return;
        }
        //如果是文件直接删除
        if (targetFile.exists() && targetFile.isFile()) {
            targetFile.delete();
            return;
        }
        File[] filesArray = targetFile.listFiles();
        if (filesArray == null || filesArray.length <= 0) {
            return;
        }
        for (File file : filesArray) {
            //如果是文件或者该目录没有子目录,就删除
            if (file.isFile() || file.listFiles().length <= 0) {
                file.delete();
            } else if (file.isDirectory()) {
                //递归删除
                deleteFile(file);
            }
        }
        //清空目录
        targetFile.delete();
    }

或者直接使用 org.apache.commons.io.FileUtils 类,调用其deleteDirectory 方法删除目录。调用方式是: FileUtils.deleteDirectory(new File(targetDir));

6. 最终的测试

public static void main(String[] args) throws IOException {
        /** 测试压缩方法1  */
        zipFile(new File("D:\\测试"), "D:/test.zip");
        String fileName = "D:/test.zip";
       unzipFile(fileName);
        String targetDir = "D:\\测试";
        File file = new File(targetDir);
        FileUtil.deleteFile(file);
    }

总结

本文详细介绍了文件的下载保存,压缩以及解压,希望对读者朋友们有所帮助。


相关文章
|
3天前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
15 2
|
6天前
|
存储 缓存 安全
在 Java 编程中,创建临时文件用于存储临时数据或进行临时操作非常常见
在 Java 编程中,创建临时文件用于存储临时数据或进行临时操作非常常见。本文介绍了使用 `File.createTempFile` 方法和自定义创建临时文件的两种方式,详细探讨了它们的使用场景和注意事项,包括数据缓存、文件上传下载和日志记录等。强调了清理临时文件、确保文件名唯一性和合理设置文件权限的重要性。
18 2
|
15天前
|
存储 安全 Java
如何保证 Java 类文件的安全性?
Java类文件的安全性可以通过多种方式保障,如使用数字签名验证类文件的完整性和来源,利用安全管理器和安全策略限制类文件的权限,以及通过加密技术保护类文件在传输过程中的安全。
|
17天前
|
存储 Java API
Java实现导出多个excel表打包到zip文件中,供客户端另存为窗口下载
Java实现导出多个excel表打包到zip文件中,供客户端另存为窗口下载
25 4
|
19天前
|
Java 数据格式 索引
使用 Java 字节码工具检查类文件完整性的原理是什么
Java字节码工具通过解析和分析类文件的字节码,检查其结构和内容是否符合Java虚拟机规范,确保类文件的完整性和合法性,防止恶意代码或损坏的类文件影响程序运行。
|
19天前
|
Java API Maven
如何使用 Java 字节码工具检查类文件的完整性
本文介绍如何利用Java字节码工具来检测类文件的完整性和有效性,确保类文件未被篡改或损坏,适用于开发和维护阶段的代码质量控制。
|
Java 大数据 Apache
|
Java Windows 移动开发
|
Java
JAVA读取文件的几种方法
喜欢的朋友可以关注下,粉丝也缺。 InputStreamReader+BufferedReader读取字符串 InputStreamReader 将字节流转换为字符流。
1290 0