Java文件拷贝的几种方式

一、前言

        文件拷贝(传输)涉及到Java中的输入和输出流(InputStream,OutputStream),FileChannel等知识点,把文件拷贝学明白了,IO流的相关知识点在头脑中也会更加清晰。这篇博客介绍几种文件拷贝的方法,其中有一些方法是较为底层的(说白了就是自己手写的方法)在实际生产环境中不推荐使用,但是通过这些较为底层的方法才能更好的理解 “流” 的操作过程,有一些方法是JDK提供的、还有一些方法是各种工具类提供的。JDK提供的和各种工具类提供的,建议在生产环境中使用。

        此篇博客主要分为传统的阻塞IO(Blocking I/O)实现的文件拷贝和基于NIO(No-Blocking I/O)的FileChannel方式实现的文件拷贝。

        题外话:既然别人已经提供了优秀的文件拷贝方法,我们没有必要去重复造轮子,但是!如果想用好这些工具类,前提是你要明白底层原理。只有这样,当你使用那些优秀的工具类时才能游刃有余地去使用,并且看别人的源码时还可以学习一些编码的新知识,如果只是囫囵吞枣的用别人提供的工具,当时是解决了你的问题,但是总感觉用的一头雾水】

二、BIO拷贝文件的几种方式

1. 拷贝文件 - V1【生产环境不推荐使用】

        最原始的文件拷贝方式(使用BIO的方式),也没有使用 try-with-resources 方式管理资源,生产环境不推荐使用。

java">    private static void copyFileV1() throws IOException {// 源文件File sourceFile = new File("test.txt");// 目标文件File targetFile = new File("test_copy.txt");FileInputStream fis = new FileInputStream(sourceFile);FileOutputStream fos = new FileOutputStream(targetFile);System.out.println("fis.available(): " + fis.available());byte[] buffer = new byte[1024];int length;// int total = 0;// int number = 0;while ((length = fis.read(buffer)) != -1) {// total += length;// number++;fos.write(buffer, 0, length);}// System.out.println("total bytes: " + total);// System.out.println("number: " + number);fos.close();fis.close();}

 2. 拷贝文件 - V2 【生产环境不推荐使用】

        使用BIO的方式,使用try-with-resources的方式释放资源,生产环境不推荐使用。

java">    private static void copyFileV2() throws IOException {// 源文件File sourceFile = new File("test.txt");// 目标文件File targetFile = new File("test_copy.txt");try (FileInputStream fis = new FileInputStream(sourceFile);FileOutputStream fos = new FileOutputStream(targetFile)) {byte[] buffer = new byte[1024];int length;while ((length = fis.read(buffer)) != -1) {fos.write(buffer, 0, length);}}}

3. 拷贝文件 - V3. 使用JDK提供的Files.copy方法 【生产环境推荐使用】

        使用JDK提供的Files.copy工具类。JDK 提供的 Files.copy() 方法本身并没有特定的文件大小限制。这个方法是用于从源通道复制到目标通道,通常用于文件拷贝。拷贝操作的效率和限制更多地取决于底层的文件系统、可用内存、JVM堆大小以及操作系统的限制等因素。

        然而,对于大文件的拷贝,使用 Files.copy() 方法可能不是最高效的方式,因为它可能会涉及将整个文件内容加载到内存中。对于大文件,推荐的做法是使用带缓冲区的分块读取和写入,这样可以减少内存消耗并提高性能,参考【3.  拷贝文件 - V2】。

java">    private static void copyFileV3() throws IOException {Path sourceFile = Paths.get("test.txt");Path targetFile = Paths.get("test_copy.txt");Files.copy(sourceFile, targetFile);//Files.copy(sourceFile, targetFile, StandardCopyOption.COPY_ATTRIBUTES);}

4. 拷贝文件 - V4. 使用Hutool工具提供的文件拷贝方法 【生产环境推荐使用】

        需要导入hutool工具包依赖,如下。其【底层使用的是JDK提供的Files工具类】。

<dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.8</version>
</dependency>
java">    private static void copyFileV4() {// 源文件File sourceFile = new File("test.txt");// 目标文件File targetFile = new File("test_copy.txt");FileUtil.copy(sourceFile, targetFile, false);}

5.  拷贝文件 - V5. 使用google提供的 guava 中的工具类 【生产环境推荐使用】

        需要导入 guava工具类。如下。【底层使用的和 copyFileV2 一样的方式,但是使用这个工具类的好处是可以帮我们自动释放流资源, 底层创建的缓冲数组大小是8192个字节】

<dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>19.0</version>
</dependency>
java">    private static void copyFileV5() throws IOException {// 源文件File sourceFile = new File("test.txt");// 目标文件File targetFile = new File("test_copy.txt");com.google.common.io.Files.copy(sourceFile, targetFile);}

 三、基于NIO的FileChannel 拷贝文件的几种方式

1.  拷贝文件 - V1. 【生产环境不推荐使用】

        使用FileChannel拷贝文件(最基本的使用方式),没有考虑文件大小问题 。

java">    private static void v1() throws IOException {// 获取文件输入流File file = new File("test.txt");FileInputStream fileInputStream = new FileInputStream(file);// 从文件输入流获取通道FileChannel inputStreamChannel = fileInputStream.getChannel();// 获取文件输出流FileOutputStream fileOutputStream = new FileOutputStream("test_copy.txt");// 从文件输出流获取通道FileChannel outputStreamChannel = fileOutputStream.getChannel();// 创建ByteBuffer, 文件内容不大,这里的演示就一次性读取,部分多次读取了ByteBuffer byteBuffer = ByteBuffer.allocate((int) file.length());// 把输入流通道的数据读取到数据缓冲区inputStreamChannel.read(byteBuffer);// 切换成读模式byteBuffer.flip();// 把缓冲区(ByteBuffer)中的数据写入到输出流通道outputStreamChannel.write(byteBuffer);// 关闭资源fileOutputStream.close();fileInputStream.close();outputStreamChannel.close();inputStreamChannel.close();}

2.  拷贝文件 - V2. 【生产环境不推荐使用】

        相比于上面的示例,这个示例代码考虑了文件的大小,使用缓冲区一次读取1024个字节的数据。

java">    private static void v2_1() {try (FileInputStream fileInputStream = new FileInputStream("test.txt");FileChannel inputStreamChannel = fileInputStream.getChannel();FileOutputStream fileOutputStream = new FileOutputStream("test_copy.txt");FileChannel outputStreamChannel = fileOutputStream.getChannel()) {ByteBuffer byteBuffer = ByteBuffer.allocate(1024);while ((inputStreamChannel.read(byteBuffer)) != -1) {byteBuffer.flip(); // Prepare the buffer for writingoutputStreamChannel.write(byteBuffer);byteBuffer.clear(); // Prepare the buffer for reading again}System.out.println("Large file copied successfully.");} catch (IOException e) {throw new RuntimeException(e);}}

        这个示例和上面的v2_1方法是一样的,只不过获取FileChannel的方式不一样。 

java">    private static void v2_2() {Path source = Paths.get("test.txt");Path target = Paths.get("test_copy.txt");try (FileChannel sourceChannel = FileChannel.open(source, StandardOpenOption.READ);FileChannel targetChannel = FileChannel.open(target, StandardOpenOption.WRITE, StandardOpenOption.CREATE)) {ByteBuffer buffer = ByteBuffer.allocate(8 * 1024); // 8 KB bufferwhile (sourceChannel.read(buffer) != -1) {buffer.flip(); // Prepare the buffer for writingtargetChannel.write(buffer);buffer.clear(); // Prepare the buffer for reading again}System.out.println("Large file copied successfully.");} catch (IOException e) {e.printStackTrace();}}

 3.  拷贝文件 - V3. 【生产环境推荐使用】

        使用FileChannel的transferTo方法拷贝文件(文件传输,效率比自己写的更高,JDK中方法transferTo的底层都会使用零拷贝进行优化) 【transferTo方法一次性传递的文件大小上限是2G,所以此代码考虑了如果要拷贝的文件大小超过2G的问题。】

java">    private static void v3() {try (FileInputStream fileInputStream = new FileInputStream("test.txt");FileChannel inputStreamChannel = fileInputStream.getChannel();FileOutputStream fileOutputStream = new FileOutputStream("test_copy.txt");FileChannel outputStreamChannel = fileOutputStream.getChannel()) {long size = inputStreamChannel.size();// left变量表示还剩余多少字节要传递for (long left = size; left > 0; ) {log.info("position:{},left:{}", (size - left), left);left = left - inputStreamChannel.transferTo((size - left), left, outputStreamChannel);}} catch (IOException e) {throw new RuntimeException(e);}}


http://www.ppmy.cn/server/90099.html

相关文章

第15周 Zookeeper分布式锁与变种多级缓存

1. Zookeeper介绍 1.1 介绍 1.2 应用场景简介 1.3 zookeeper工作原理 1.4 zookeeper特点

MICA:面向复杂嵌入式系统的混合关键性部署框架

背景 在嵌入式场景中&#xff0c;虽然 Linux 已经得到了广泛应用&#xff0c;但并不能覆盖所有需求&#xff0c;例如高实时、高可靠、高安全的场合。这些场合往往是实时操作系统的用武之地。有些应用场景既需要 Linux 的管理能力、丰富的生态&#xff0c;又需要实时操作系统的高…

【css】实现扫光特效

对于要重点突出的元素&#xff0c;我们经常可以看到它上面打了一个从左到右的斜向扫光&#xff0c;显得元素亮闪闪的&#xff01;类似于下图的亮光动效 关键步骤 伪元素设置position :absolute【也可以不用伪元素&#xff0c;直接创建一个absolute元素盖在上面】设置渐变line…

WINUI——Microsoft.UI.Xaml.Markup.XamlParseException:“无法找到与此错误代码关联的文本。

开发环境 VS2022 .net core6 问题现象 在Canvas内的子控件要绑定Canvas的兄弟控件的一个属性&#xff0c;在运行时出现了下述报错。 可能原因 在 WinUI&#xff08;特别是用于 UWP 或 Windows App SDK 的版本&#xff09;中&#xff0c;如果你尝试在 XAML 中将 Canvas 内的…

MongoDB - 集合方法 db.collection.find()

文章目录 1. query 参数2. projection 参数2.1 仅返回指定的字段2.2 排除字段2.3 显式排除_id字段 3. 修改游标行为3.1 为结果集中的文档排序3.2 限制要返回的文档数量3.3 控制结果集的起点3.4 组合游标方法 db.collection.find( <query>, <projection>, <optio…

3d动画软件blender如何汉化?(最新版本4.2)

前言 Blender是一个非常强大的3d动画软件&#xff0c;总能受到大量工作者的青睐。 但是&#xff0c;对于新手来说&#xff08;尤其是英语学渣&#xff09;&#xff0c;语言是个难事。大部分blender打开时都是英文&#xff0c;对新手使用具有一定的障碍。因此&#xff0c;我们需…

Tableau入门|数据可视化与仪表盘搭建

原视频链接&#xff08;up:戴戴戴师兄&#xff09;&#xff0c;文章为笔者的自学笔记&#xff0c;用于复习回顾&#xff0c;原视频下方有原up整理的笔记&#xff0c;更加直观便捷。因为视频中间涉及的细节较多&#xff0c;建议一边操作&#xff0c;一边学习。 整体介绍 可视化…

日记审计遵守合规安全要求

一、什么是日志审计系统 日记审计系统是一种用于记录、监视和分析系统日志的工具或系统。它主要用于帮助组织实时监控与分析各种事件和行为的日志记录&#xff0c;以便检测潜在的安全威胁&#xff0c;了解系统性能和进行故障排除。日志审计系统通常能够收集、存储和分析来自各…

划分型dp,CF 1935C - Messenger in MAC

一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 1935C - Messenger in MAC 二、解题报告 1、思路分析 比较简单的思路是反悔贪心&#xff0c;这里不展开说了&#xff0c;来说一下dp的做法 由于式子里面带绝对值&#xff0c;很烦&#xff0c;我们将pair按…

WebKit的WebGCCollect Garbage API:浏览器性能优化的新纪元

WebKit的WebGCCollect Garbage API&#xff1a;浏览器性能优化的新纪元 在Web开发中&#xff0c;内存管理是一个不可忽视的方面。WebKit的WebGCCollect Garbage API提供了一种机制&#xff0c;允许开发者手动触发垃圾收集&#xff0c;优化内存使用和提高应用性能。这项API虽然…

探索 Kubernetes 持久化存储之 Longhorn 初窥门径

作者&#xff1a;运维有术星主 在 Kubernetes 生态系统中&#xff0c;持久化存储扮演着至关重要的角色&#xff0c;它是支撑业务应用稳定运行的基石。对于那些选择自建 Kubernetes 集群的运维架构师而言&#xff0c;选择合适的后端持久化存储解决方案是一项至关重要的选型决策。…

算法:数值算法

矩阵乘法 定义与性质 矩阵乘法是线性代数中的一个基本运算&#xff0c;它涉及到两个矩阵的点积运算。给定两个矩阵 A&#xff08;mn&#xff09;和 B&#xff08;np&#xff09;&#xff0c;它们的乘积 C&#xff08;mp&#xff09;定义为&#xff1a; 其中&#xff0c; Cij …

Angular由一个bug说起之八:实践中遇到的一个数据颗粒度的问题

互联网产品离不开数据处理&#xff0c;数据处理有一些基本的原则包括&#xff1a;准确性、‌完整性、‌一致性、‌保密性、‌及时性。‌ 准确性&#xff1a;是数据处理的首要目标&#xff0c;‌确保数据的真实性和可靠性。‌准确的数据是进行分析和决策的基础&#xff0c;‌因此…

最小可行产品 (MVP):从概念到实践

引言 在当今快节奏的商业环境中&#xff0c;产品开发必须能够迅速适应市场的变化。最小可行产品&#xff08;Minimum Viable Product, MVP&#xff09;作为一种敏捷开发策略&#xff0c;已经被广泛采纳。本文将探讨MVP的概念、重要性以及如何成功实施MVP策略。 什么是MVP&…

Elasticsearch:Golang ECS 日志记录 - zerolog

ECS 记录器是你最喜欢的日志库的格式化程序/编码器插件。它们可让你轻松地将日志格式化为与 ECS 兼容的 JSON。在本教程中&#xff0c;我将详述如何 编码器以 JSON 格式记录日志&#xff0c;并以 ECS 错误格式处理错误字段的记录。 默认情况下&#xff0c;会添加以下字段&…

响应式编程框架Reactor之 Flux 和 Mono 的介绍和区别

Flux和Mono在Reactor框架中都是响应式编程模型的重要概念,它们在处理异步数据流时发挥着重要作用,两者之间也存在一些差异。 Mono的介绍 基本概念: Mono是Reactor中的一个类,它表示一个异步的单个值或零个值的结果。Mono可以看作是一个特殊的Publisher,用于产生数据流,…

SQL Server数据库管理(一)掌握SQL Server数据库管理:从安装到优化的全面指南

文章目录 掌握SQL Server数据库管理&#xff1a;从安装到优化的全面指南1. SQL Server数据库概述1.1 关系型数据库的基本概念1.2 SQL Server的特点 2. SQL Server的安装2.1 安装准备2.2 安装步骤 3. 数据库的基本管理3.1 数据库的创建3.2 数据表的管理创建数据表修改数据表删除…

WordPress文章标题定制化前缀插件

引言 在当今互联网的海洋中&#xff0c;吸引读者眼球的第一步往往始于文章标题的设计。对于WordPress博主而言&#xff0c;如何让每篇文章的标题更加个性化和吸引人&#xff0c;成为了一项重要的任务。传统的自定义CSS方法虽然可行&#xff0c;但其繁琐的操作和有限的美学效果…

k8s云原生技术栈(脑图)

Kubernetes (K8s) 是一种开源的容器编排引擎&#xff0c;用于自动化应用程序容器的部署、扩展和操作。它由Google设计并捐赠给Cloud Native Computing Foundation&#xff08;CNCF&#xff09;进行维护。Kubernetes 提供了一个强大的平台&#xff0c;用于构建和管理容器化应用程…

数据结构之深入理解简单选择排序:原理、实现与示例(C,C++)

文章目录 一、简单选择排序原理二、C/C代码实现总结&#xff1a; 在计算机科学中&#xff0c;排序算法是一种非常基础且重要的算法。简单选择排序&#xff08;Selection Sort&#xff09;作为其中的一种&#xff0c;因其实现简单、易于理解而受到许多初学者的喜爱。本文将详细介…