相关文章推荐
失恋的牙膏  ·  VBA实战技巧10: ...·  4 月前    · 
老实的烈酒  ·  openpyxl.utils.excepti ...·  1 年前    · 
面冷心慈的手套  ·  unity vector3 ...·  1 年前    · 

renameTo是File的方法,move是Files的方法,Files是java8引入的,Android8.0(26)才支持了java8,也就是说Files.move方法在Android8.0及以上才能使用。move比renameTo更健壮些,简单来说就是renameTo只有在一个存储分区上才能成功,通俗来讲就是在同一个存储卡上,详细解释看源码注释。

不过两者有相同之处,以目录操作为例,如果目标目录已经存在,move和renameTo都会失效,renameTo返回false,move报FileSystemException异常

10-24 11:13:05.555 12575 12790 W System.err: java.nio.file.FileSystemException: /storage/emulated/0/shvdownload/video/SohuVideoGallery -> /storage/emulated/0/Movies/SHVideo: Directory not empty
10-24 11:13:05.555 12575 12790 W System.err: 	at sun.nio.fs.UnixCopyFile.move(UnixCopyFile.java:396)
10-24 11:13:05.555 12575 12790 W System.err: 	at sun.nio.fs.UnixFileSystemProvider.move(UnixFileSystemProvider.java:262)
10-24 11:13:05.555 12575 12790 W System.err: 	at java.nio.file.Files.move(Files.java:1395)
10-24 11:13:05.555 12575 12790 W System.err: 	at com.sohu.sohuvideo.sdk.android.storage.SHDataMigrateUtil.moveData(SHDataMigrateUtil.java:148)
10-24 11:13:05.555 12575 12790 W System.err: 	at com.sohu.sohuvideo.sdk.android.storage.SHDataMigrateUtil.migrateShareVideos(SHDataMigrateUtil.java:97)
10-24 11:13:05.555 12575 12790 W System.err: 	at com.sohu.sohuvideo.sdk.android.storage.SHDataMigrateUtil.access$300(SHDataMigrateUtil.java:24)
10-24 11:13:05.555 12575 12790 W System.err: 	at com.sohu.sohuvideo.sdk.android.storage.SHDataMigrateUtil$1.run(SHDataMigrateUtil.java:43)
10-24 11:13:05.555 12575 12790 W System.err: 	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
10-24 11:13:05.555 12575 12790 W System.err: 	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)

所以,目标目录存在的话,只能通过内容拷贝的方式了。

    private void moveData(File source, File target) {
        long start = System.currentTimeMillis();
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            Path sourceP = source.toPath();
            Path targetP = target.toPath();
            if (target.exists()) {
                copyDir(source, target);
                LogUtils.i(TAG, "moveData copyDir");
            } else {
                try {
                    Files.move(sourceP, targetP, StandardCopyOption.ATOMIC_MOVE);
                    LogUtils.i(TAG, "moveData Files.move");
                } catch (IOException e) {
                    e.printStackTrace();
        } else {
            if (target.exists()) {
                copyDir(source, target);
                LogUtils.i(TAG, "moveData copyDir");
            } else {
                boolean result = source.renameTo(target);
                LogUtils.i(TAG, "moveData renameTo result " + result);
        long end = System.currentTimeMillis();
        long val = end - start;
        LogUtils.i(TAG, "migrate data take time " + val +" from " + source.getAbsolutePath() + " to " + target.getAbsolutePath());
    private void copyDir(File source, File target) {
        if (source == null || target == null)
            return;
        if (!target.exists())
            target.mkdirs();
        String sourceS = source.getPath();
        String targetS = target.getPath();
        String[] paths = source.list();
        for (String tmp : paths) {
            File tmpFile = new File(sourceS + File.separator + tmp);
            File newFile = new File(targetS + File.separator + tmp);
            if (tmpFile.isDirectory()) {
                copyDir(tmpFile, newFile);
            } else {
                FileUtils.copy(tmpFile, newFile);

Files的move方法,可以对文件夹操作,不过需要分情况:move可以去移动一个空目录,如果是在同一个分区操作,被移动的目录有内容,也是可以移动的,其实就是对目录名进行了重命名;如果不在同一个分区,就会fail。如果目标目录存在的话,move也会fail。

在实际开发中得测试,下面在Android不同系统上move表现就不一样。

特别注意!!!

Files.move操作在Android不同系统版本上有区别,这个很坑呀!!!

在Android11上,

/storage/emulated/0/shvdownload/video/SohuVideoGallery to /storage/emulated/0/Movies/SHVideo

SohuVideoGallery目录中有文件,SHVideo目录不存在,move可以成功

/storage/emulated/0/sohu/SohuVideo/data to /storage/emulated/0/Android/data/com.sohu.sohuvideo/files/data

data目录中有文件,files/data目录不存在,move失败:

StandardCopyOption.ATOMIC_MOVE的move报AtomicMoveNotSupportedException异常

StandardCopyOption.REPLACE_EXISTING的move报DirectoryNotEmptyException异常

java.nio.file.DirectoryNotEmptyException: /storage/emulated/0/sohu/SohuVideo/data
at sun.nio.fs.UnixCopyFile.move(UnixCopyFile.java:498)
at sun.nio.fs.UnixFileSystemProvider.move(UnixFileSystemProvider.java:262)
at java.nio.file.Files.move(Files.java:1395)
at com.sohu.sohuvideo.sdk.android.storage.SHDataMigrateUtil.moveData(SHDataMigrateUtil.java:148)
at com.sohu.sohuvideo.sdk.android.storage.SHDataMigrateUtil.migrateUIDData(SHDataMigrateUtil.java:80)
at com.sohu.sohuvideo.sdk.android.storage.SHDataMigrateUtil.access$400(SHDataMigrateUtil.java:24)
at com.sohu.sohuvideo.sdk.android.storage.SHDataMigrateUtil$1.run(SHDataMigrateUtil.java:44)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:923)

在Android10及一下系统,上面操作都是正常的!!!可能是Android 11对Android/data目录有了限制吧!

Files的move方法也是可以移动文件的,需要source和target都必须是文件路径,而且target路径中的文件夹都得存在。否则会报错

java.nio.file.NoSuchFileException: /storage/emulated/0/AAAA/sohuusf -> /storage/emulated/0/AAAC/sohuusf
        at sun.nio.fs.UnixCopyFile.move(UnixCopyFile.java:457)
        at sun.nio.fs.UnixFileSystemProvider.move(UnixFileSystemProvider.java:262)
        at java.nio.file.Files.move(Files.java:1395)
        at com.zy.myapplication7.FileUtils.moveData(FileUtils.java:115)
        at com.zy.myapplication7.FirstFragment$1.onClick(FirstFragment.java:41)
        at android.view.View.performClick(View.java:7258)
        at com.google.android.material.button.MaterialButton.performClick(MaterialButton.java:992)
        at android.view.View.performClickInternal(View.java:7220)
        at android.view.View.access$3800(View.java:821)
        at android.view.View$PerformClick.run(View.java:27712)
        at android.os.Handler.handleCallback(Handler.java:883)
        at android.os.Handler.dispatchMessage(Handler.java:100)
        at android.os.Looper.loop(Looper.java:237)
        at android.app.ActivityThread.main(ActivityThread.java:7840)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:985)

移动(或复制)文件(或文件夹)的方法

    public static List<String> copyData(File source, File target, boolean existJump) {
        return copyData(source, target, existJump, false);
     * 复制文件或文件夹
     * @param source 可以是文件也可以是文件夹
     * @param target 必须是文件夹
     * @param existJump 目标文件已经存在时,true表示跳过复制,false表示文件重命名(文件名加数字递增的方式)复制
     * @param delete 复制成功后是否删除原文件
     * @return 返回所有复制成功后的目标文件路径
    private static List<String> copyData(File source, File target, boolean existJump, boolean delete) {
        if (source == null || target == null) {
            return null;
        if (!target.exists()) {
            target.mkdirs();
        } else {
            if (!target.isDirectory()) {
                throw new IllegalArgumentException("target must is directory!!!");
        String sourceS = source.getPath();
        String targetS = target.getPath();
        String[] paths;
        if (source.isDirectory()) {
            paths = source.list();
        } else {
            sourceS = source.getParent();
            paths = new String[]{source.getName()};
        List<String> successList = new ArrayList<>();
        for (String tmp : paths) {
            File tmpFile = new File(sourceS + File.separator + tmp);
            File newFile = new File(targetS + File.separator + tmp);
            if (tmpFile.isDirectory()) {
                List<String> middleList =copyData(tmpFile, newFile, existJump, delete);
                if (!middleList.isEmpty())
                    successList.addAll(middleList);
            } else {
                if (newFile.exists()) {
                    //不跳过
                    if (!existJump) {
                        // 递增文件名
                        for (int i = 1;;i++) {
                            String[] arr = tmp.split("\\.");
                            String tmp2 = arr[0] + "("+ i + ")";
                            if (arr.length > 1) {
                                tmp2 += "." + arr[1];
                            newFile = new File(target, tmp2);
                            if(!newFile.exists())
                                break;
                        String successPath = copyFileCompat(tmpFile, newFile);
                        if (successPath != null && successPath.length() != 0) {
                            successList.add(successPath);
                            if (delete) {
                                tmpFile.delete();
                } else {
                    String successPath = copyFileCompat(tmpFile, newFile);
                    if (successPath != null && successPath.length() != 0) {
                        successList.add(successPath);
                        if (delete) {
                            tmpFile.delete();
        if (delete && source.isDirectory() && (source.list() == null || source.list().length == 0)) {
            source.delete();
        return successList;
    private static String copyFileCompat(File oldFile, File newFile) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            try {
                Files.copy(oldFile.toPath(), newFile.toPath());
                return newFile.getPath();
            } catch (IOException e) {
                e.printStackTrace();
        } else {
            boolean isCopySuccess = FileUtils.copy(oldFile, newFile);
            if (isCopySuccess) {
                return newFile.getPath();
        return null;
     * 复制文件
     * @param sourceFile
     * @param destFile
     * @return
    public static boolean copy(File sourceFile, File destFile) {
        FileInputStream fis = null;
        FileOutputStream fos = null;
        try {
            fis = new FileInputStream(sourceFile);
            fos = new FileOutputStream(destFile);
            byte[] bytes = new byte[BUFFER];
            int ret = -1;
            while ((ret = fis.read(bytes)) != -1) {
                fos.write(bytes, 0, ret);
            return true;
        } catch (IOException e) {
            LogUtils.e(TAG, e);
        } finally {
            try {
                if (fis != null) {
                    fis.close();
                if (fos != null) {
                    fos.close();
            } catch (IOException e) {
                LogUtils.e(TAG, e);
        return false;
     * 移动文件或文件夹
     * @param source 可以是文件也可以是文件夹
     * @param target 必须是文件夹
     * @param existJump 目标文件已经存在时,true表示跳过,false表示文件重命名(文件名加数字递增的方式)移动
     * @return 返回所有移动成功后的目标文件路径
    public static List<String> moveData(File source, File target, boolean existJump) {
        if (source == null || target == null) {
            return null;
        if (!target.exists()) {
            target.mkdirs();
        } else {
            if (!target.isDirectory()) {
                throw new IllegalArgumentException("target must is directory!!!");
        String sourceS = source.getPath();
        String targetS = target.getPath();
        String[] paths;
        if (source.isDirectory()) {
            paths = source.list();
        } else {
            sourceS = source.getParent();
            paths = new String[]{source.getName()};
        List<String> successList = new ArrayList<>();
        for (String tmp : paths) {
            File tmpFile = new File(sourceS + File.separator + tmp);
            File newFile = new File(targetS + File.separator + tmp);
            if (tmpFile.isDirectory()) {
                List<String> middleList =moveData(tmpFile, newFile, existJump);
                if (!middleList.isEmpty())
                    successList.addAll(middleList);
            } else {
                if (newFile.exists()) {
                    //不跳过
                    if (!existJump) {
                        // 递增文件名
                        for (int i = 1;;i++) {
                            String[] arr = tmp.split("\\.");
                            String tmp2 = arr[0] + "("+ i + ")";
                            if (arr.length > 1) {
                                tmp2 += "." + arr[1];
                            newFile = new File(target, tmp2);
                            if(!newFile.exists())
                                break;
                        String successPath = moveFileCompat(tmpFile, newFile);
                        if (successPath != null && successPath.length() != 0) {
                            successList.add(successPath);
                } else {
                    String successPath = moveFileCompat(tmpFile, newFile);
                    if (successPath != null && successPath.length() != 0) {
                        successList.add(successPath);
        if (source.isDirectory() && (source.list() == null || source.list().length == 0)) {
            source.delete();
        return successList;
    private static String moveFileCompat(File oldFile, File newFile) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            try {
                Files.move(oldFile.toPath(), newFile.toPath());
                return newFile.getPath();
            } catch (IOException e) {
                e.printStackTrace();
        } else {
            boolean isCopySuccess = oldFile.renameTo(newFile);
            if (isCopySuccess) {
                return newFile.getPath();
        return null;

Moving a File or Directory

Java: Move Directory containing files and directories to new path

move定义

文件重命名知多少

Moving a file on android

Android 复制文件最快方法

Windows上可靠的File.renameTo()替代方法?

请慎用java的File#renameTo(File)方法(转)

内置的只是C rename(2)函数JavaScript包装器,它不支持在分区或设备之间移动文件。 该模块就是您期望的fs.rename() 。 Promise API。 支持跨分区和设备移动文件。 (可选)防止覆盖现有文件。 为您创建不存在的目标目录。 $ npm install move-file const moveFile = require ( 'move-file' ) ; ( async ( ) => { await moveFile ( 'source/unicorn.png' , 'destination/unicorn.png' ) ; console . log ( 'The file has been moved' ) ; } ) ( ) ; moveFile(源,目标,选项?) 返回一个Promise 做文件管理相关项目有个需求需要对单个或多个文件进行重命名,这就可能会出现名称重复的情况;还有复制的时候,如果粘贴的地方已存在相同名称文件,也需要进行重命名。 相仿思想: 我们知道在电脑上复制粘贴同一文件(夹)到同一路径下的时候,系统会帮我们自动生成新的副本(Copy)名 MAC是文件名 + ” ” + 数字递增 + 后缀 Windows是文件名 + ” 示例代码: File f = new File("D:\\test\\test.txt"); boolean flag = f.renameTo( new File("D:\\test\\test\\test02.txt") ); System.out.println( flag ); 此操作与平台(platform-dependent)相关,可能失败,所以最好接收boolean类型返回值以加以判断,且如果参数中的路径文件已存在,不能覆盖,return false public void rename(){ File srcFile = new File("D:/test/123.zip"); boolean b = srcFile.renameTo(new File("D:/test/456.zip")); System.out.println(b);
可以使用 `fs` 模块的 `fs.createWriteStream()` 和 `fs.rename()` 方法来下载文件并重命名。 首先,使用 `https` 模块或 `http` 模块发起一个 HTTP 请求,然后将请求响应流传递给 `fs.createWriteStream()` 方法。这样就可以将请求响应内容写入文件了。 示例代码如下: const fs = require('fs'); const https = require('https'); https.get('https://example.com/file.txt', (response) => { response.pipe(fs.createWriteStream('file.txt')); 在文件下载完成后,可以使用 `fs.rename()` 方法将文件重命名。示例代码如下: fs.rename('file.txt', 'new_file.txt', (err) => { if (err) throw err; console.log('文件重命名完成'); 注意:在重命名文件之前,应该确保文件已经下载完成。因此,可以在 `response.pipe()` 方法的回调函数中调用 `fs.rename()` 方法。 希望这些信息对你有帮助。