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()` 方法。
希望这些信息对你有帮助。