You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java调用FileUtils.copyDirectory复制SMB网络文件速度远低于系统文件管理器

解决Java FileUtils复制SMB共享NAS文件速度慢的问题

我之前处理过类似的SMB共享文件复制性能问题,用Apache Commons IO的FileUtils.copyDirectory确实会比Windows资源管理器或Nautilus慢不少,最高只有8MB/s的速度,主要根源在这几点:

  • FileUtils默认使用较小的缓冲区(默认是4KB),而且是单线程逐文件复制,完全没利用到SMB协议的批量传输、多线程优化特性
  • 系统自带的文件管理器底层调用的是系统级SMB客户端,做了大量协议层面的优化——比如调整SMB窗口大小、多线程并发传输、缓存策略优化这些,天生就比通用Java IO实现高效

下面给你几个实用的解决办法,按推荐程度排序:

1. 直接调用系统级复制命令(最推荐,速度和资源管理器一致)

如果追求和系统工具一样的传输速度,直接调用系统自带的复制工具是最优解,这些工具专门针对SMB做了深度优化:

Windows系统:用Robocopy

Robocopy是Windows原生的高性能文件复制工具,对SMB共享的优化非常到位,还支持断点续传:

import java.io.IOException;

public class SmbCopyUtil {
    public static void copyWithRobocopy(String nasDir, String localDir) throws IOException {
        // /E 复制所有子目录(包括空目录);/J 用无缓冲I/O复制大文件,适合SMB
        String command = String.format("robocopy \"%s\" \"%s\" /E /J", nasDir, localDir);
        Process process = Runtime.getRuntime().exec(command);
        
        try {
            int exitCode = process.waitFor();
            // Robocopy的退出码0-7都属于正常完成,大于8才是真正的错误
            if (exitCode > 8) {
                throw new IOException("Robocopy复制失败,退出码:" + exitCode);
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new IOException("复制操作被中断", e);
        }
    }
}

Linux/macOS系统:用Rsync

Rsync是类Unix系统下的经典工具,对网络文件传输的优化极强,速度基本和Nautilus持平:

import java.io.IOException;

public class SmbCopyUtil {
    public static void copyWithRsync(String nasDir, String localDir) throws IOException {
        // -a 归档模式(保留权限、时间戳等元数据);-v 显示进度;-h 人类可读格式
        String command = String.format("rsync -avh \"%s/\" \"%s\"", nasDir, localDir);
        Process process = Runtime.getRuntime().exec(command);
        
        try {
            int exitCode = process.waitFor();
            if (exitCode != 0) {
                throw new IOException("Rsync复制失败,退出码:" + exitCode);
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new IOException("复制操作被中断", e);
        }
    }
}

2. 优化Java IO的复制逻辑,增大缓冲区

如果不想依赖系统命令,可以自定义复制方法,把缓冲区调大(比如256KB或512KB),减少SMB的IO交互次数:

import java.io.*;

public class FastSmbCopy {
    // 调整为256KB缓冲区,比FileUtils默认的4KB大很多
    private static final int BUFFER_SIZE = 256 * 1024;

    public static void copyDirectory(File srcNasDir, File destLocalDir) throws IOException {
        if (!destLocalDir.exists()) {
            destLocalDir.mkdirs();
        }

        File[] files = srcNasDir.listFiles();
        if (files == null) return;

        for (File file : files) {
            File destFile = new File(destLocalDir, file.getName());
            if (file.isDirectory()) {
                copyDirectory(file, destFile);
            } else {
                copyLargeFile(file, destFile);
            }
        }
    }

    private static void copyLargeFile(File src, File dest) throws IOException {
        try (BufferedInputStream in = new BufferedInputStream(new FileInputStream(src), BUFFER_SIZE);
             BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(dest), BUFFER_SIZE)) {
            
            byte[] buffer = new byte[BUFFER_SIZE];
            int bytesRead;
            while ((bytesRead = in.read(buffer)) != -1) {
                out.write(buffer, 0, bytesRead);
            }
            out.flush();
        }
    }
}

这个方法能把速度提升到接近系统工具的60%-70%,适合必须用纯Java实现的场景。

3. 使用Java NIO的Files类

Java NIO的Files.copy底层调用系统级IO接口,比传统IO更高效,尤其是对网络文件系统:

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.io.IOException;

public class NioSmbCopy {
    public static void copyDirectory(String nasPath, String localPath) throws IOException {
        Files.walk(Paths.get(nasPath))
             .forEach(srcPath -> {
                 // 构建目标路径,保持目录结构
                 Path destPath = Paths.get(localPath, 
                     srcPath.subpath(Paths.get(nasPath).getNameCount(), srcPath.getNameCount()).toString());
                 try {
                     Files.copy(srcPath, destPath, StandardCopyOption.REPLACE_EXISTING);
                 } catch (IOException e) {
                     e.printStackTrace();
                 }
             });
    }
}

额外的SMB客户端优化

除了代码调整,还可以修改系统的SMB客户端设置进一步提速:

  • Windows:打开网卡属性→Internet协议版本4→高级→WINS,启用「TCP/IP上的NetBIOS」;也可以通过注册表调整SMB的接收缓冲区大小(搜“Windows SMB 性能优化”能找到具体步骤)
  • Linux:修改/etc/samba/smb.conf,添加socket options = TCP_NODELAY IPTOS_LOWDELAY SO_RCVBUF=131072 SO_SNDBUF=131072,增大缓冲区

内容的提问来源于stack exchange,提问作者Jon_C

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 04:22:38