I tried to write specific bytes to certain position of a file using FileChannel. But actually the file shrink to the last position where I write change. I do it like this:
Path path = Paths.get("I://music - Copy.mp3");
System.out.println(Files.size(path)/1024 + "KB");
try (FileChannel chan = new FileOutputStream(path.toFile()).getChannel()) {
chan.position(1024 * 1024);
ByteBuffer b = ByteBuffer.allocate(1024);
chan.write(b);
System.out.println("Write 1KB of data");
}
System.out.println(Files.size(path)/1024 + "KB");
and this is the output I get:
3670KB
Write 1KB of data
1025KB
Can anybody tell me where it goes wrong??
FileOutputStream truncates the file to zero length when not in append mode. It does not overwrite a file's contents so much as it throws away the contents and starts over. You can verify this by calling chan.size()
after creating the channel, which will give you 0. [1]
FileChannels can be advanced past the end of a file and told to write there; this causes the file size to increase to position + bytes_written (emphasis mine):
Setting the position to a value that is greater than the current size is legal but does not change the size of the entity. [..] A later attempt to write bytes at such a position will cause the entity to grow to accommodate the new bytes; the values of any bytes between the previous end-of-file and the newly-written bytes are unspecified.
So while it looks like the FileChannel is cutting off your file after writing, it is the FileOutputStream truncating to 0 length, and then the FileChannel expanding it again.
To prevent this from happening, avoid using a FileOutputStream to create a channel. You have a Path, so you can call Files.newByteChannel
or FileChannel.open
:
Path path = Paths.get("I://music - Copy.mp3");
System.out.println(Files.size(path)/1024 + "KB");
// pick either:
try (FileChannel chan = FileChannel.open(path, StandardOpenOption.WRITE)) {
try (SeekableByteChannel chan = Files.newByteChannel(path, StandardOpenOption.WRITE)) {
chan.position(1024 * 1024);
ByteBuffer b = ByteBuffer.allocate(1024);
chan.write(b);
System.out.println("Write 1KB of data");
}
System.out.println(Files.size(path)/1024 + "KB");
[1] Note that programs outside the JVM, such as a file explorer, may not indicate this until you flush or close the stream.