Copy and move files in Java, explanation and comparison of different approaches
Asked Answered
D

2

1

I implement a file manipulation functionality, and I paid attention that Java provides multiple techniques to copy and move files. Below you can find code snippets, briefly describing these approaches:

Approach #1:

File from = new File(src.getPath());
File to = new File(dst.getPath());

from.renameTo(to);

Approach #2:

FileChannel inChannel = new FileInputStream(src).getChannel();
FileChannel outChannel = new FileOutputStream(dst).getChannel();

inChannel.transferTo(0, inChannel.size(), outChannel);

Approach #3:

InputStream in = getContentResolver().openInputStream(selectedImageUri);
OutputStream out = new FileOutputStream("/sdcard/wallpapers/" + wall);

byte[] buffer = new byte[1024];
int read;

while ((read = in.read(buffer)) != -1) {
    out.write(buffer, 0, read);
}

Approach #4:

import static java.nio.file.StandardCopyOption.*;

Files.copy(source, target, REPLACE_EXISTING);

All these approaches work, but I can't grasp when should I use each of them? What are the pros and cons of each of these methods, especially from the performance and the reliability points of view? Is there any specific scenario when I have to prefer one technique over another?

Dwan answered 29/6, 2015 at 18:36 Comment(2)
Note: approach one isn't really a file copyPrivity
@TameHog, right. Fixed.Dwan
M
1

We can divide your four approaches into two types:

  1. Use a built-in standard library method (such as File.renameTo() and Files.move()).
  2. Do the work ourselves - by copying bytes from source to target.

First, note that File doesn't have a copy method, so you only have one option for built-in, standard library method when you're talking about copy.

Also note that "do the work ourselves" when renaming is going to be very bad - you're going to copy the entire file, then delete the old file. Not a good or efficient approach. In most cases, renaming/moving within the same filesystem requires just changing file metadata without actually touching the content, so it's really a lot better to use a standard library.

So you have two cases:

Renaming

The options are really using either File.renameTo() or Files.move(). No point in using streams and copying data.

File is an outdated class. It shouldn't really be used anymore. There is an excellent explanation why, which sums up to the fact that File doesn't give you any information when any of its standard methods fail, whereas Files provides you with very accurate exceptions when that happens.

Copying

You have two choices - either use Files.copy() or one of the "do it yourself" approaches.

By far, if what you are copying are actual files, your choice should be Files.copy(). There is no need to re-invent the wheel. It does exactly what you want, is well documented, you're not likely to introduce bugs accidentally. And yes, it's very efficient.

Files.copy() relies on underlying "providers" for its operation. What it means is that there are specialized vendor (or operating system) specific classes that do the operation that is the most efficient for that filesystem. Whether it's a Linux filesystem or a Windows one, the copy will be optimized for it. There are even providers for specialized cases, such as zip files, so you can copy files inside a zip, jar or war file using Files.copy() - which is a lot more complicated if you try the "do it yourself" approach.

Besides, Files.copy() checks lots of things that you might forget when you write "your own" copy. For example, did you remember to check that the file that you are reading from and the file you are writing to are not the same file? It could cause serious trouble. Files.copy() does it. It checks permissions, it checks if the target of the copy is a directory, and so on. So it's very reliable.

So why do you have the option to do "your own"? Because well, Java is a general-purpose language. You have the option to read from a file, the option to write to a file, so you can write your own "copy" method. That doesn't mean you should.

Note that in your "approach #3", the "source" file is not actually a file! It's produced from an Image URI, which means it could be a network source. When your source is not a file, but a stream or channel based on a socket, database BLOB, web server request etc., you can't really use Files.copy(). This is where you'd need to write your own.

Actually, Files also has options for copying from a file to an OutputStream or from an InputStream to a file, so if one side of the copy is a stream and the other a file, you can use that. It will be readable, safe, and throw meaningful exceptions.

So write your own copy:

  • when you need to move data from sources to targets which are not files,
  • when you need to filter or process the data somehow rather than copy as-is from source to target,
  • when you are using old versions of Java, prior to 1.7. In this case, channels would probably be better than streams.
Medius answered 29/6, 2015 at 20:32 Comment(2)
thank you for the detailed explanation. From your explanation it leads that the best way to copy a file is Files.copy();, I would like to know about the comparison of Files.copy(); vs. «approach #2».Dwan
If you mean from the point of view of performance, don't worry about it too much. They are the same order of magnitude, and you should not worry about a millisecond here or there unless in your particular project, after completing and writing, you find a bottleneck in this particular point. Readability, bug potential, and simplicity always trump performance when designing.Medius
B
2

It is already discussed enough here and the following is from here

Your first approach is File rename that has nothing to do with File copy

java.io.File class doesn’t have any shortcut method to copy file from source to destination.

1. Using Stream: This is the conventional way of file copy in java, here we create two Files, source and destination. Then we create InputStream from source and write it to destination file using OutputStream.

2. Using java.nio.channels.FileChannel: Java NIO classes were introduced in Java 1.4 and FileChannel can be used to copy file in java. According to transferFrom() method javadoc, this way of copy file is supposed to be faster than using Streams to copy files.

3. Using Apache Commons IO: Apache Commons IO FileUtils.copyFile(File srcFile, File destFile) can be used to copy file in java. If you are already using Apache Commons IO in your project, it makes sense to use this for code simplicity. Internally it uses Java NIO FileChannel, so you can avoid this wrapper method if you are not already using it for other functions.

4. Java 7 Files class: If you are working on Java 7, you can use Files class copy() method to copy file in java. It uses File System providers to copy the files.

Now to see which one of these methods is more efficient we will copy a large file[1 GB] using each one of them in a simple program. To avoid any performance speedups from caching we are going to use four different source files and four different destination files.{Refer code in link}

Time taken by FileStreams Copy = 127572360
Time taken by FileChannels Copy = 10449963
Time taken by Java7 Files Copy = 10808333
Time taken by Apache Commons IO Copy = 17971677

From the output it’s clear that Stream Copy is the best way to copy File in Java. FileChannels is the best way to copy large files. If you work with even larger files you will notice a much bigger speed difference

Bolide answered 29/6, 2015 at 18:54 Comment(2)
That's not how one writes a microbenchmark. I wouldn't trust that result.Medius
Ya I accept it.I quoted it from the webpage where the comparison is doneBolide
M
1

We can divide your four approaches into two types:

  1. Use a built-in standard library method (such as File.renameTo() and Files.move()).
  2. Do the work ourselves - by copying bytes from source to target.

First, note that File doesn't have a copy method, so you only have one option for built-in, standard library method when you're talking about copy.

Also note that "do the work ourselves" when renaming is going to be very bad - you're going to copy the entire file, then delete the old file. Not a good or efficient approach. In most cases, renaming/moving within the same filesystem requires just changing file metadata without actually touching the content, so it's really a lot better to use a standard library.

So you have two cases:

Renaming

The options are really using either File.renameTo() or Files.move(). No point in using streams and copying data.

File is an outdated class. It shouldn't really be used anymore. There is an excellent explanation why, which sums up to the fact that File doesn't give you any information when any of its standard methods fail, whereas Files provides you with very accurate exceptions when that happens.

Copying

You have two choices - either use Files.copy() or one of the "do it yourself" approaches.

By far, if what you are copying are actual files, your choice should be Files.copy(). There is no need to re-invent the wheel. It does exactly what you want, is well documented, you're not likely to introduce bugs accidentally. And yes, it's very efficient.

Files.copy() relies on underlying "providers" for its operation. What it means is that there are specialized vendor (or operating system) specific classes that do the operation that is the most efficient for that filesystem. Whether it's a Linux filesystem or a Windows one, the copy will be optimized for it. There are even providers for specialized cases, such as zip files, so you can copy files inside a zip, jar or war file using Files.copy() - which is a lot more complicated if you try the "do it yourself" approach.

Besides, Files.copy() checks lots of things that you might forget when you write "your own" copy. For example, did you remember to check that the file that you are reading from and the file you are writing to are not the same file? It could cause serious trouble. Files.copy() does it. It checks permissions, it checks if the target of the copy is a directory, and so on. So it's very reliable.

So why do you have the option to do "your own"? Because well, Java is a general-purpose language. You have the option to read from a file, the option to write to a file, so you can write your own "copy" method. That doesn't mean you should.

Note that in your "approach #3", the "source" file is not actually a file! It's produced from an Image URI, which means it could be a network source. When your source is not a file, but a stream or channel based on a socket, database BLOB, web server request etc., you can't really use Files.copy(). This is where you'd need to write your own.

Actually, Files also has options for copying from a file to an OutputStream or from an InputStream to a file, so if one side of the copy is a stream and the other a file, you can use that. It will be readable, safe, and throw meaningful exceptions.

So write your own copy:

  • when you need to move data from sources to targets which are not files,
  • when you need to filter or process the data somehow rather than copy as-is from source to target,
  • when you are using old versions of Java, prior to 1.7. In this case, channels would probably be better than streams.
Medius answered 29/6, 2015 at 20:32 Comment(2)
thank you for the detailed explanation. From your explanation it leads that the best way to copy a file is Files.copy();, I would like to know about the comparison of Files.copy(); vs. «approach #2».Dwan
If you mean from the point of view of performance, don't worry about it too much. They are the same order of magnitude, and you should not worry about a millisecond here or there unless in your particular project, after completing and writing, you find a bottleneck in this particular point. Readability, bug potential, and simplicity always trump performance when designing.Medius

© 2022 - 2024 — McMap. All rights reserved.