Как сделать копию файла в Android?

178

В моем приложении я хочу сохранить копию определенного файла с другим именем (которое я получаю от пользователя)

Мне действительно нужно открыть содержимое файла и записать его в другой файл?

Каков наилучший способ сделать это?

ТАК КАК
источник
До Java7, я думаю, что ответ да, вы делаете. stackoverflow.com/questions/106770/… .
Пол Грайм
проверьте более раннюю запись
Deepak

Ответы:

327

Чтобы скопировать файл и сохранить его по своему пути назначения, вы можете использовать метод ниже.

public static void copy(File src, File dst) throws IOException {
    InputStream in = new FileInputStream(src);
    try {
        OutputStream out = new FileOutputStream(dst);
        try {
            // Transfer bytes from in to out
            byte[] buf = new byte[1024];
            int len;
            while ((len = in.read(buf)) > 0) {
                out.write(buf, 0, len);
            }
        } finally {
            out.close();
        }
    } finally {
        in.close();
    }
}

В API 19+ вы можете использовать Java Automatic Resource Management:

public static void copy(File src, File dst) throws IOException {
    try (InputStream in = new FileInputStream(src)) {
        try (OutputStream out = new FileOutputStream(dst)) {
            // Transfer bytes from in to out
            byte[] buf = new byte[1024];
            int len;
            while ((len = in.read(buf)) > 0) {
                out.write(buf, 0, len);
            }
        }
    }
}
Ракши
источник
8
Спасибо. после удара головой я обнаружил, что проблема заключается в отсутствии разрешения на запись во внешнее хранилище. сейчас работает нормально.
AS
8
@ mohitum007 если файл не удается скопировать, возникает исключение. используйте блок try catch при вызове метода.
Нима Г
9
Если выдается исключение, потоки не будут закрыты до тех пор, пока не будут собраны сборщики мусора, и это не хорошо . Рассмотрите возможность их закрытия finally.
Панг
1
@Pang. Ты прав. Что еще хуже, должна вызываться in / out.close (), иначе в базовой ОС будет утечка ресурсов, поскольку GC никогда не закроет дескрипторы открытого файла. GC не может закрывать ресурсы ОС, внешние по отношению к JVM, такие как дескрипторы файлов или сокеты. Они должны всегда закрываться в предложении finally программно или использовать новый синтаксис try-with-resource: docs.oracle.com/javase/tutorial/essential/exceptions/…
earizon
4
Пожалуйста, закройте оба потока внутри, наконец, если есть исключение, память ваших потоков не будет собрана.
pozuelog
121

Кроме того, вы можете использовать FileChannel для копирования файла. Это может быть быстрее, чем метод байтового копирования при копировании большого файла. Вы не можете использовать его, если ваш файл больше 2 ГБ.

public void copy(File src, File dst) throws IOException {
    FileInputStream inStream = new FileInputStream(src);
    FileOutputStream outStream = new FileOutputStream(dst);
    FileChannel inChannel = inStream.getChannel();
    FileChannel outChannel = outStream.getChannel();
    inChannel.transferTo(0, inChannel.size(), outChannel);
    inStream.close();
    outStream.close();
}
NullNoname
источник
3
TransferTo может выдать исключение, и в этом случае вы оставляете потоки открытыми. Также как Панг и Нима прокомментировали принятый ответ.
Виктор Брешан
Кроме того, TransferTo должен вызываться внутри цикла, так как он не гарантирует, что он переведет всю запрошенную сумму.
Виктор Брешан
Я попробовал ваше решение , и оно не для меня, за исключением java.io.FileNotFoundException: /sdcard/AppProj/IMG_20150626_214946.jpg: open failed: ENOENT (No such file or directory)на FileOutputStream outStream = new FileOutputStream(dst);этапе. По тексту я понимаю, что файл не существует, поэтому я проверяю его и dst.mkdir();при необходимости вызываю , но это все равно не помогает. Я тоже пытался проверить, dst.canWrite();и он вернулся false. Может ли это источник проблемы? И да, у меня есть <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>.
Майк Б.
1
@ ViktorBrešan После API 19 вы можете использовать автоматическое управление ресурсами Java, определяя try ( FileInputStream inStream = new FileInputStream(src); FileOutputStream outStream = new FileOutputStream(dst) ) {
входные и выходные
Есть ли способ заставить это решение опубликовать свой прогресс onProgressUpdate, чтобы я мог показать его в ProgressBar? В принятом решении я могу вычислить прогресс в цикле while, но не вижу, как это сделать здесь.
Джордж Безерра
31

Удлинение Kotlin для этого

fun File.copyTo(file: File) {
    inputStream().use { input ->
        file.outputStream().use { output ->
            input.copyTo(output)
        }
    }
}
Дима Ростопира
источник
Это самый краткий, но гибкий ответ. Более простые ответы не учитывают URI, открытые через contentResolver.openInputStream(uri).
AjahnCharles
6
Котлин - это любовь, Котлин - это жизнь!
Дев Аггарвал
17

Это работало хорошо для меня

public static void copyFileOrDirectory(String srcDir, String dstDir) {

    try {
        File src = new File(srcDir);
        File dst = new File(dstDir, src.getName());

        if (src.isDirectory()) {

            String files[] = src.list();
            int filesLength = files.length;
            for (int i = 0; i < filesLength; i++) {
                String src1 = (new File(src, files[i]).getPath());
                String dst1 = dst.getPath();
                copyFileOrDirectory(src1, dst1);

            }
        } else {
            copyFile(src, dst);
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}

public static void copyFile(File sourceFile, File destFile) throws IOException {
    if (!destFile.getParentFile().exists())
        destFile.getParentFile().mkdirs();

    if (!destFile.exists()) {
        destFile.createNewFile();
    }

    FileChannel source = null;
    FileChannel destination = null;

    try {
        source = new FileInputStream(sourceFile).getChannel();
        destination = new FileOutputStream(destFile).getChannel();
        destination.transferFrom(source, 0, source.size());
    } finally {
        if (source != null) {
            source.close();
        }
        if (destination != null) {
            destination.close();
        }
    }
}
Боян Ксенеман
источник
13

Это просто на Android O (API 26), как вы видите:

  @RequiresApi(api = Build.VERSION_CODES.O)
  public static void copy(File origin, File dest) throws IOException {
    Files.copy(origin.toPath(), dest.toPath());
  }
смотреть искоса
источник
10

Возможно, уже слишком поздно для ответа, но самый удобный способ

FileUtils«s

static void copyFile(File srcFile, File destFile)

например, это то, что я сделал

`

private String copy(String original, int copyNumber){
    String copy_path = path + "_copy" + copyNumber;
        try {
            FileUtils.copyFile(new File(path), new File(copy_path));
            return copy_path;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

`

stopBugs
источник
20
FileUtils не существует изначально в Android.
Berga
2
Но есть библиотека, созданная Apache, которая делает это и многое другое: commons.apache.org/proper/commons-io/javadocs/api-2.5/org/…
Алессандро Муцци,
7

Намного проще теперь с Kotlin:

 File("originalFileDir", "originalFile.name")
            .copyTo(File("newFileDir", "newFile.name"), true)

trueили falseдля перезаписи файла назначения

https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.io/java.io.-file/copy-to.html

Бландел
источник
Не все так просто, если ваш файл исходит из намерения галереи Uri.
AjahnCharles
Прочитайте приведенные ниже комментарии к этому ответу: «Этот ответ активно вреден и не заслуживает тех голосов, которые он получил. Он потерпит неудачу, если Uri является контентом: // или любым другим нефайловым Uri». (Я сделал upvote - я просто хотел уточнить, что это не серебряная пуля)
AjahnCharles
5

Вот решение, которое фактически закрывает потоки ввода / вывода, если при копировании возникает ошибка. Это решение использует методы Apache Commons IO IOUtils для копирования и обработки закрытия потоков.

    public void copyFile(File src, File dst)  {
        InputStream in = null;
        OutputStream out = null;
        try {
            in = new FileInputStream(src);
            out = new FileOutputStream(dst);
            IOUtils.copy(in, out);
        } catch (IOException ioe) {
            Log.e(LOGTAG, "IOException occurred.", ioe);
        } finally {
            IOUtils.closeQuietly(out);
            IOUtils.closeQuietly(in);
        }
    }
SBerg413
источник
Это выглядит просто
Сергей Бурлака
2

в котлине, просто:

val fileSrc : File = File("srcPath")
val fileDest : File = File("destPath")

fileSrc.copyTo(fileDest)
Sodino
источник