Setiap file di sistem operasi punya metadata: ukuran, waktu modifikasi, pemilik, grup, izin akses, dan lain-lain. Bab ini membahas cara membaca dan menulis atribut file di Kotlin menggunakan API NIO.2 (BasicFileAttributes, PosixFileAttributes, Files.getAttribute) serta perbandingannya dengan API klasik java.io.File. Pemahaman ini penting untuk utility yang inspeksi metadata, scheduler yang mengecek waktu modifikasi, atau security tool yang memverifikasi izin.
Atribut Dasar: BasicFileAttributes #
BasicFileAttributes adalah antarmuka yang berisi atribut umum di semua sistem file. Diakses via Files.readAttributes(path, BasicFileAttributes::class.java). Atributnya mencakup:
size()— ukuran file dalam byte.isRegularFile(),isDirectory(),isSymbolicLink(),isOther()— tipe file.lastModifiedTime(),creationTime(),lastAccessTime()— timestamp sebagaiFileTime.fileKey()— identifier unik file (filesystem-dependent).
import java.io.File
import java.nio.file.Files
import java.nio.file.Paths
import java.nio.file.Path
import java.nio.file.attribute.BasicFileAttributes
import java.nio.file.attribute.PosixFileAttributes
import java.nio.file.attribute.PosixFilePermission
import java.nio.file.attribute.FileTime
import java.time.Instant
import java.time.LocalDateTime
import java.time.ZoneOffset
const val DEMO_DIR = "/tmp/kotlin-tutorial-attributes"
fun main() {
File(DEMO_DIR).deleteRecursively()
File(DEMO_DIR).mkdirs()
// Siapkan file demo dengan ukuran tetap
val file = File("$DEMO_DIR/data.txt")
file.writeText("isi file dengan beberapa byte data")
// Set waktu deterministik untuk lastModified, creationTime, lastAccessTime
val waktuTetap = Instant.parse("2026-01-15T08:30:00Z")
val waktuTetapFile = FileTime.from(waktuTetap)
file.setLastModified(waktuTetap.toEpochMilli())
val pathAwal: Path = file.toPath()
Files.setAttribute(pathAwal, "creationTime", waktuTetapFile)
Files.setAttribute(pathAwal, "lastAccessTime", waktuTetapFile)
println("=== 1. BasicFileAttributes: atribut dasar ===")
val path: Path = file.toPath()
val basic = Files.readAttributes(path, BasicFileAttributes::class.java)
println("size = ${basic.size()} bytes")
println("isRegularFile = ${basic.isRegularFile}")
println("isDirectory = ${basic.isDirectory}")
println("isSymbolicLink = ${basic.isSymbolicLink}")
println("isOther = ${basic.isOther}")
println("fileKey.class = ${basic.fileKey()?.javaClass?.simpleName ?: "null"}")
println()
println("=== 2. FileTime: waktu modifikasi dan akses ===")
println("lastModifiedTime = ${basic.lastModifiedTime().toString().substringBefore('.')}")
println("lastAccessTime = ${basic.lastAccessTime().toString().substringBefore('.')}")
println("(creationTime tidak ditampilkan: tidak semua FS mendukung setAttribute)")
println()
println("=== 3. Files.getAttribute: akses per-atribut ===")
val size = Files.getAttribute(path, "size")
val isReg = Files.getAttribute(path, "isRegularFile")
val isDir = Files.getAttribute(path, "isDirectory")
val lastMod = Files.getAttribute(path, "lastModifiedTime")
println("size = $size")
println("isRegularFile = $isReg")
println("isDirectory = $isDir")
println("lastModifiedTime = ${(lastMod as FileTime).toString().substringBefore('.')}")
println()
println("=== 4. Files.setAttribute: ubah atribut ===")
val waktuBaru = FileTime.from(Instant.parse("2026-02-20T10:15:30Z"))
Files.setAttribute(path, "lastModifiedTime", waktuBaru)
val lastModSetelah = Files.getAttribute(path, "lastModifiedTime") as FileTime
println("lastModifiedTime setelah setAttribute = ${lastModSetelah.toString().substringBefore('.')}")
println()
println("=== 5. Konversi FileTime ke LocalDateTime ===")
val ldt = LocalDateTime.ofInstant(basic.lastModifiedTime().toInstant(), ZoneOffset.UTC)
println("LocalDateTime (UTC) = $ldt")
println()
println("=== 6. PosixFileAttributes: atribut POSIX ===")
val posix = Files.readAttributes(path, PosixFileAttributes::class.java)
println("owner = ${posix.owner().name}")
println("group = ${posix.group().name}")
println("permissions (sorted) = ${posix.permissions().sortedBy { it.name }}")
println()
println("=== 7. Files.getPosixFilePermissions: izin POSIX ===")
val perms = Files.getPosixFilePermissions(path).sortedBy { it.name }
println("perms (sorted set) = $perms")
println("perms (string rwx) = ${posixPermissionsToString(perms.toSet())}")
println()
println("=== 8. File (java.io) attributes: alternative API ===")
println("File.length = ${file.length()}")
println("File.lastModified (epoch millis) = ${file.lastModified()}")
println("File.canRead = ${file.canRead()}")
println("File.canWrite = ${file.canWrite()}")
println("File.canExecute = ${file.canExecute()}")
println("File.isHidden = ${file.isHidden}")
println("File.totalSpace (parent) = ${File(DEMO_DIR).totalSpace > 0}")
println("File.freeSpace (parent) = ${File(DEMO_DIR).freeSpace > 0}")
println()
println("=== 9. Direktori atribut ===")
val dir = File(DEMO_DIR)
val dirPath = dir.toPath()
val dirBasic = Files.readAttributes(dirPath, BasicFileAttributes::class.java)
println("dir isDirectory (NIO) = ${dirBasic.isDirectory}")
println("dir isRegularFile (NIO) = ${dirBasic.isRegularFile}")
println("dir size (NIO) = ${dirBasic.size()} (ukuran direktori entry)")
println()
println("=== 10. Bersih-bersih ===")
File(DEMO_DIR).deleteRecursively()
println("demo dir dihapus = ${!File(DEMO_DIR).exists()}")
}
fun posixPermissionsToString(perms: Set<PosixFilePermission>): String {
val sb = StringBuilder()
sb.append(permChar(perms, PosixFilePermission.OWNER_READ, 'r'))
sb.append(permChar(perms, PosixFilePermission.OWNER_WRITE, 'w'))
sb.append(permChar(perms, PosixFilePermission.OWNER_EXECUTE, 'x'))
sb.append(permChar(perms, PosixFilePermission.GROUP_READ, 'r'))
sb.append(permChar(perms, PosixFilePermission.GROUP_WRITE, 'w'))
sb.append(permChar(perms, PosixFilePermission.GROUP_EXECUTE, 'x'))
sb.append(permChar(perms, PosixFilePermission.OTHERS_READ, 'r'))
sb.append(permChar(perms, PosixFilePermission.OTHERS_WRITE, 'w'))
sb.append(permChar(perms, PosixFilePermission.OTHERS_EXECUTE, 'x'))
return sb.toString()
}
fun permChar(perms: Set<PosixFilePermission>, p: PosixFilePermission, c: Char): Char =
if (p in perms) c else '-'Output yang Dihasilkan:
=== 1. BasicFileAttributes: atribut dasar ===
size = 34 bytes
isRegularFile = true
isDirectory = false
isSymbolicLink = false
isOther = false
fileKey.class = UnixFileKey
=== 2. FileTime: waktu modifikasi dan akses ===
lastModifiedTime = 2026-01-15T08:30:00Z
lastAccessTime = 2026-01-15T08:30:00Z
(creationTime tidak ditampilkan: tidak semua FS mendukung setAttribute)
=== 3. Files.getAttribute: akses per-atribut ===
size = 34
isRegularFile = true
isDirectory = false
lastModifiedTime = 2026-01-15T08:30:00Z
=== 4. Files.setAttribute: ubah atribut ===
lastModifiedTime setelah setAttribute = 2026-02-20T10:15:30Z
=== 5. Konversi FileTime ke LocalDateTime ===
LocalDateTime (UTC) = 2026-01-15T08:30
=== 6. PosixFileAttributes: atribut POSIX ===
owner = z
group = z
permissions (sorted) = [GROUP_READ, GROUP_WRITE, OTHERS_READ, OWNER_READ, OWNER_WRITE]
=== 7. Files.getPosixFilePermissions: izin POSIX ===
perms (sorted set) = [GROUP_READ, GROUP_WRITE, OTHERS_READ, OWNER_READ, OWNER_WRITE]
perms (string rwx) = rw-rw-r--
=== 8. File (java.io) attributes: alternative API ===
File.length = 34
File.lastModified (epoch millis) = 1771582530000
File.canRead = true
File.canWrite = true
File.canExecute = false
File.isHidden = false
File.totalSpace (parent) = true
File.freeSpace (parent) = true
=== 9. Direktori atribut ===
dir isDirectory (NIO) = true
dir isRegularFile (NIO) = false
dir size (NIO) = 4096 (ukuran direktori entry)
=== 10. Bersih-bersih ===
demo dir dihapus = trueMari kita bedah bagian-bagian penting:
- Bagian 1 (BasicFileAttributes):
Files.readAttributes(path, BasicFileAttributes::class.java)membaca semua atribut dalam satu syscall — lebih efisien daripada memanggilFiles.getAttributebeberapa kali.fileKey()mengembalikan objek unik per file (di Linux berupaUnixFileKeyberisi device ID + inode). Penting: fileKey bisa berbeda-beda antar sistem file, jadi tidak semua path memiliki fileKey yang berguna. - Bagian 2 (FileTime):
FileTimeadalah representasi timestamp di NIO.2. Bisa dikonversi keInstantvia.toInstant(). Penting:creationTimedi banyak filesystem Linux tidak bisa di-set viasetAttribute— panggilan berhasil tetapi nilai tidak berubah. Inilah alasan kita tidak menampilkannya di output. - Bagian 3 (
Files.getAttribute): alternatif satu-atribut. Berguna saat hanya butuh satu atribut — tidak perlu membaca semua. Format string seperti"size","isRegularFile","lastModifiedTime","posix:owner"(untuk POSIX-specific). - Bagian 4 (
Files.setAttribute): menulis atribut. Tidak semua atribut bisa diubah (mis.sizetidak bisa di-set langsung; gunakantruncatefile). Yang umum diubah:lastModifiedTime,lastAccessTime. - Bagian 5 (LocalDateTime): konversi
FileTimekeLocalDateTimeuntuk display.ZoneOffset.UTCuntuk zona waktu UTC; ganti denganZoneId.systemDefault()untuk zona lokal. - Bagian 6 (PosixFileAttributes): subtype
BasicFileAttributesuntuk sistem POSIX (Linux, macOS). Menambahkanowner()(UserPrincipal),group()(GroupPrincipal), danpermissions()(Set<PosixFilePermission>). Pemilik file di sandbox adalahz— pengguna yang menjalankan proses. - Bagian 7 (
Files.getPosixFilePermissions): shortcut untuk membaca hanya izin. Formatrw-rw-r--adalah standar ls -l: 3 grup (owner, group, others) × 3 karakter (read, write, execute). File demo memilikirw-(owner),rw-(group),r--(others). - Bagian 8 (java.io.File API): alternatif klasik.
File.length(),File.lastModified()(epoch millis),canRead/canWrite/canExecute. Lebih sederhana tetapi tidak mendukung owner, group, atau izin POSIX granular. - Bagian 9 (atribut direktori): direktori punya
size()yang biasanya 4096 byte (satu blok filesystem di Linux ext4).isDirectory = true,isRegularFile = false. Size direktori tidak mencerminkan ukuran isi — untuk itu gunakanwalkTopDown().map { it.length() }.sum().
Atribut Lain di BasicFileAttributes #
Tabel ringkas atribut yang tersedia:
| Atribut | Tipe | Bisa di-set? |
|---|---|---|
size | Long (bytes) | Tidak (gunakan truncate) |
isRegularFile | Boolean | Tidak |
isDirectory | Boolean | Tidak |
isSymbolicLink | Boolean | Tidak |
lastModifiedTime | FileTime | Ya |
lastAccessTime | FileTime | Ya (filesystem-dependent) |
creationTime | FileTime | Ya (filesystem-dependent) |
fileKey | Object? | Tidak |
POSIX Permissions #
Izin POSIX terdiri dari 9 bagian dalam 3 grup:
| Owner | Group | Others | Karakter |
|---|---|---|---|
| read | read | read | r (atau -) |
| write | write | write | w (atau -) |
| execute | execute | execute | x (atau -) |
Format string rw-rw-r-- dibaca: owner rw-, group rw-, others r--. Angka oktal (mis. 664) merepresentasikan izin yang sama.
Referensi Lanjutan #
- Java SE 21 Files
— dokumentasi
FilesJava SE 21 untukreadAttributesdanBasicFileAttributes - Kotlin stdlib kotlin.io.path
— paket
kotlin.io.pathdengan extension atribut file idiomatic Kotlin
Selanjutnya #
Bab berikutnya, File Permissions, akan membahas lebih dalam modifikasi izin POSIX: PosixFilePermissions.fromString("rw-r--r--"), Files.setPosixFilePermissions, dan dasar ACL di Windows.