Як зробити копію файлу в android?


178

У своєму додатку я хочу зберегти копію певного файлу з іншим ім’ям (яке я отримую від користувача)

Чи дійсно мені потрібно відкрити вміст файлу і записати його в інший файл?

Який найкращий спосіб зробити це?


Перед Java7, я думаю, що відповідь - так, ти це робиш. stackoverflow.com/questions/106770/… .
Пол Гриме


перевірити старший пост
Діпак

Відповіді:


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:

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
Дякую. після удару головою я виявив, що в програмі не було дозволу писати на зовнішній сховище. тепер це працює чудово.
ЯК

8
@ mohitum007, якщо файл не вдалося скопіювати, викидається виняток. використовуйте блок спробу лову під час виклику методу.
Німа Г

9
Якщо буде викинуто виняток, потоки не закриватимуться, поки вони не збирають сміття, і це не добре . Подумайте про їх закриття finally.
Панг

1
@Pang. Ти правий. Що ще гірше, потрібно викликати in / out.close (), інакше буде витік ресурсів у базовій ОС, оскільки GC ніколи не закриє дескриптори відкритого файлу. GC не може закрити ресурси ОС, що знаходяться зовні JVM, як дескриптори файлів або сокети. Вони завжди мають бути закриті в остаточному пункті програмно або використовувати пробний ресурс нового синтаксису: docs.oracle.com/javase/tutorial/essential/exceptions/…
earizon

4
Нарешті, закрийте обидва потоку всередині, якщо є Excepcion, пам'ять ваших потоків не буде зібрана.
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();
}

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

Розширення Котліна для нього

fun File.copyTo(file: File) {
    inputStream().use { input ->
        file.outputStream().use { output ->
            input.copyTo(output)
        }
    }
}

Це найбільш стисла, але гнучка відповідь. Простіші відповіді не відповідають обліку URI, відкритих через contentResolver.openInputStream(uri).
AjahnCharles

6
Котлін - це любов, Котлін - це життя!
Dev Aggarwal

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;
    }

`


20
FileUtils не існує початково в Android.
Берга

2
Але є бібліотека Apache, яка робить це і багато іншого: commons.apache.org/proper/commons-io/javadocs/api-2.5/org/…
Alessandro Muzzi

7

Набагато простіше зараз з Котліном:

 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


Не так просто, якщо ваш файл походить від наміру галереї Урі.
AjahnCharles

Прочитайте коментарі нижче, що відповідають: "Ця відповідь є активно шкідливою і не заслуговує на голоси, які вона отримує. Не вдається, якщо Uri - це вміст: // або будь-який інший нефайловий урі." (Я зробив схвалення - я просто хотів уточнити, що це не срібна куля)
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);
        }
    }

Це виглядає просто
Серг Бурлака

2

в Котліні, просто:

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

fileSrc.copyTo(fileDest)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.