Breaking: Change File.copyTo contract to be similar to Files.copy from JDK8.
This commit is contained in:
@@ -159,44 +159,48 @@ private fun File.toRelativeStringOrNull(base: File): String? {
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Copies this file to the given output [dst], returning the number of bytes copied.
|
* Copies this file to the given output [target], returning the number of bytes copied.
|
||||||
*
|
*
|
||||||
* If some directories on a way to the [dst] are missing, then they will be created.
|
* If some directories on a way to the [target] are missing, then they will be created.
|
||||||
* If the [dst] file already exists, then this function will fail unless [overwrite] argument is set to `true`.
|
* If the [target] file already exists, this function will fail unless [overwrite] argument is set to `true`.
|
||||||
* Otherwise this file overwrites [dst] if it's a file to, or is written into [dst] if it's a directory.
|
|
||||||
*
|
*
|
||||||
* Note: this function fails if you call it on a directory.
|
* When [overwrite] is `true` and [target] is a directory, it is replaced only if it is empty.
|
||||||
* If you want to copy directories, use 'copyRecursively' function instead.
|
*
|
||||||
|
* If this file is a directory, it is copied without its content, i.e. an empty [target] directory is created.
|
||||||
|
* If you want to copy directory including its contents, use [copyRecursively].
|
||||||
*
|
*
|
||||||
* @param overwrite `true` if destination overwrite is allowed.
|
* @param overwrite `true` if destination overwrite is allowed.
|
||||||
* @param bufferSize the buffer size to use when copying.
|
* @param bufferSize the buffer size to use when copying.
|
||||||
* @return the number of bytes copied
|
* @return the number of bytes copied or zero if the copied file was a directory.
|
||||||
* @throws NoSuchFileException if the source file doesn't exist.
|
* @throws NoSuchFileException if the source file doesn't exist.
|
||||||
* @throws FileAlreadyExistsException if the destination file already exists and 'rewrite' argument is set to `false`.
|
* @throws FileAlreadyExistsException if the destination file already exists and 'rewrite' argument is set to `false`.
|
||||||
* @throws IOException if any errors occur while copying.
|
* @throws IOException if any errors occur while copying.
|
||||||
*/
|
*/
|
||||||
public fun File.copyTo(dst: File, overwrite: Boolean = false, bufferSize: Int = DEFAULT_BUFFER_SIZE): Long {
|
public fun File.copyTo(target: File, overwrite: Boolean = false, bufferSize: Int = DEFAULT_BUFFER_SIZE): Long {
|
||||||
if (!exists()) {
|
if (!this.exists()) {
|
||||||
throw NoSuchFileException(file = this, reason = "The source file doesn't exist")
|
throw NoSuchFileException(file = this, reason = "The source file doesn't exist")
|
||||||
} else if (isDirectory) {
|
}
|
||||||
throw IllegalArgumentException("Use copyRecursively to copy a directory $this")
|
|
||||||
} else if (dst.exists()) {
|
if (target.exists()) {
|
||||||
if (!overwrite) {
|
val stillExists = if (!overwrite) true else !target.delete()
|
||||||
|
|
||||||
|
if (stillExists) {
|
||||||
throw FileAlreadyExistsException(file = this,
|
throw FileAlreadyExistsException(file = this,
|
||||||
other = dst,
|
other = target,
|
||||||
reason = "The destination file already exists")
|
reason = "The destination file already exists")
|
||||||
} else if (dst.isDirectory && dst.listFiles().any()) {
|
|
||||||
// In this case file should be copied *into* this directory,
|
|
||||||
// no matter whether it is empty or not
|
|
||||||
return copyTo(dst.resolve(name), overwrite, bufferSize)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dst.parentFile?.mkdirs()
|
|
||||||
dst.delete()
|
if (this.isDirectory) {
|
||||||
val input = FileInputStream(this)
|
if (!target.mkdirs())
|
||||||
return input.use<FileInputStream, Long> {
|
throw FileSystemException(file = this, other = target, reason = "Failed to create target directory")
|
||||||
val output = FileOutputStream(dst)
|
return 0
|
||||||
output.use<FileOutputStream, Long> {
|
}
|
||||||
|
|
||||||
|
target.parentFile?.mkdirs()
|
||||||
|
|
||||||
|
return this.inputStream().use { input ->
|
||||||
|
target.outputStream().use { output ->
|
||||||
input.copyTo(output, bufferSize)
|
input.copyTo(output, bufferSize)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -356,42 +356,67 @@ class FilesTest {
|
|||||||
@test fun testCopyTo() {
|
@test fun testCopyTo() {
|
||||||
val srcFile = createTempFile()
|
val srcFile = createTempFile()
|
||||||
val dstFile = createTempFile()
|
val dstFile = createTempFile()
|
||||||
srcFile.writeText("Hello, World!")
|
try {
|
||||||
assertFailsWith(FileAlreadyExistsException::class) {
|
srcFile.writeText("Hello, World!")
|
||||||
srcFile.copyTo(dstFile)
|
assertFailsWith(FileAlreadyExistsException::class, "copy do not overwrite existing file") {
|
||||||
|
srcFile.copyTo(dstFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
var len = srcFile.copyTo(dstFile, overwrite = true)
|
||||||
|
assertEquals(13L, len)
|
||||||
|
assertEquals(srcFile.readText(), dstFile.readText(Charsets.UTF_8), "copy with overwrite over existing file")
|
||||||
|
|
||||||
|
assertTrue(dstFile.delete())
|
||||||
|
len = srcFile.copyTo(dstFile)
|
||||||
|
assertEquals(13L, len)
|
||||||
|
assertEquals(srcFile.readText(Charsets.UTF_8), dstFile.readText(), "copy to new file")
|
||||||
|
|
||||||
|
assertTrue(dstFile.delete())
|
||||||
|
dstFile.mkdir()
|
||||||
|
val child = File(dstFile, "child")
|
||||||
|
child.createNewFile()
|
||||||
|
assertFailsWith(FileAlreadyExistsException::class, "copy with overwrite do not overwrite non-empty dir") {
|
||||||
|
srcFile.copyTo(dstFile, overwrite = true)
|
||||||
|
}
|
||||||
|
child.delete()
|
||||||
|
|
||||||
|
srcFile.copyTo(dstFile, overwrite = true)
|
||||||
|
assertEquals(srcFile.readText(), dstFile.readText(), "copy with overwrite over empty dir")
|
||||||
|
|
||||||
|
assertTrue(srcFile.delete())
|
||||||
|
assertTrue(dstFile.delete())
|
||||||
|
|
||||||
|
assertFailsWith(NoSuchFileException::class) {
|
||||||
|
srcFile.copyTo(dstFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
srcFile.mkdir()
|
||||||
|
srcFile.resolve("somefile").writeText("some content")
|
||||||
|
dstFile.writeText("")
|
||||||
|
assertFailsWith(FileAlreadyExistsException::class, "copy dir do not overwrite file") {
|
||||||
|
srcFile.copyTo(dstFile)
|
||||||
|
}
|
||||||
|
srcFile.copyTo(dstFile, overwrite = true)
|
||||||
|
assertTrue(dstFile.isDirectory)
|
||||||
|
assertTrue(dstFile.listFiles()!!.isEmpty(), "only directory is copied, but not its content")
|
||||||
|
|
||||||
|
assertFailsWith(FileAlreadyExistsException::class, "copy dir do not overwrite dir") {
|
||||||
|
srcFile.copyTo(dstFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
srcFile.copyTo(dstFile, overwrite = true)
|
||||||
|
assertTrue(dstFile.isDirectory)
|
||||||
|
assertTrue(dstFile.listFiles()!!.isEmpty(), "only directory is copied, but not its content")
|
||||||
|
|
||||||
|
dstFile.resolve("somefile2").writeText("some content2")
|
||||||
|
assertFailsWith(FileAlreadyExistsException::class, "copy dir do not overwrite dir") {
|
||||||
|
srcFile.copyTo(dstFile, overwrite = true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
finally {
|
||||||
var len = srcFile.copyTo(dstFile, overwrite = true)
|
srcFile.deleteRecursively()
|
||||||
assertEquals(13L, len)
|
dstFile.deleteRecursively()
|
||||||
assertEquals(srcFile.readText(), dstFile.readText(Charsets.UTF_8))
|
|
||||||
|
|
||||||
assertTrue(dstFile.delete())
|
|
||||||
len = srcFile.copyTo(dstFile)
|
|
||||||
assertEquals(13L, len)
|
|
||||||
assertEquals(srcFile.readText(Charsets.UTF_8), dstFile.readText())
|
|
||||||
|
|
||||||
assertTrue(dstFile.delete())
|
|
||||||
dstFile.mkdir()
|
|
||||||
val child = File(dstFile, "child")
|
|
||||||
child.createNewFile()
|
|
||||||
srcFile.copyTo(dstFile, overwrite = true)
|
|
||||||
assertEquals(13L, len)
|
|
||||||
val copy = dstFile.resolve(srcFile.name)
|
|
||||||
assertEquals(srcFile.readText(), copy.readText())
|
|
||||||
|
|
||||||
assertTrue(srcFile.delete())
|
|
||||||
assertTrue(child.delete() && copy.delete() && dstFile.delete())
|
|
||||||
|
|
||||||
assertFailsWith(NoSuchFileException::class) {
|
|
||||||
srcFile.copyTo(dstFile)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
srcFile.mkdir()
|
|
||||||
|
|
||||||
assertFailsWith(IllegalArgumentException::class) {
|
|
||||||
srcFile.copyTo(dstFile)
|
|
||||||
}
|
|
||||||
srcFile.delete()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@test fun copyToNameWithoutParent() {
|
@test fun copyToNameWithoutParent() {
|
||||||
|
|||||||
Reference in New Issue
Block a user