简体   繁体   中英

FileChannel.open(path, CREATE|CREATE_NEW) without WRITE option throws NoSuchFileException

I had the following code:


  @Nonnull
  @SneakyThrows
  private Pair<InputStream, Long> probeSize(@Nonnull final InputStream image) {
    final String tmpId = UUID.randomUUID().toString();
    final File probeFile = new File(tmpDir, tmpId + ".jpg");

    try (final FileChannel outChannel = FileChannel.open(probeFile.toPath(), CREATE);
         final ReadableByteChannel innChannel = Channels.newChannel(image)) {
      outChannel.transferFrom(innChannel, 0, Long.MAX_VALUE);
    }

    final Long fileSize = probeFile.length();
    return Pair.of(new FileInputStream(probeFile), fileSize);
  }

This code consistently threw the following exception:

Caused by: java.nio.file.NoSuchFileException: /tmp/4bbc9008-e91c-4f18-b0f2-c61eed35066e.jpg
    at sun.nio.fs.UnixException.translateToIOException(Unknown Source)
    at sun.nio.fs.UnixException.rethrowAsIOException(Unknown Source)
    at sun.nio.fs.UnixException.rethrowAsIOException(Unknown Source)
    at sun.nio.fs.UnixFileSystemProvider.newFileChannel(Unknown Source)

looking at the javadoc of FileChannel.open(path, option) and the associated StandardOpenOption , there is no documentation that alludes to the fact that, to create a file, you must also open it for write.

The only options that work:

  • FileChannel.open(probeFile.toPath(), CREATE, WRITE)
  • FileChannel.open(probeFile.toPath(), CREATE_NEW, WRITE)

I only determined this by going through the UnixChannelFactory.newFileChannel and noticed the following:

UnixChannelFactory:

protected static FileDescriptor open(int dfd,
                                         UnixPath path,
                                         String pathForPermissionCheck,
                                         Flags flags,
                                         int mode)
        throws UnixException
    {
        // map to oflags
        int oflags;
        if (flags.read && flags.write) {
            oflags = O_RDWR;
        } else {
            oflags = (flags.write) ? O_WRONLY : O_RDONLY;
        }
        if (flags.write) {
            if (flags.truncateExisting)
                oflags |= O_TRUNC;
            if (flags.append)
                oflags |= O_APPEND;

            // create flags
            if (flags.createNew) {
                byte[] pathForSysCall = path.asByteArray();

                // throw exception if file name is "." to avoid confusing error
                if ((pathForSysCall[pathForSysCall.length-1] == '.') &&
                    (pathForSysCall.length == 1 ||
                    (pathForSysCall[pathForSysCall.length-2] == '/')))
                {
                    throw new UnixException(EEXIST);
                }
                oflags |= (O_CREAT | O_EXCL);
            } else {
                if (flags.create)
                    oflags |= O_CREAT;
            }
        }

Which shows that, unless you specify WRITE option, the file will never be created.

Is this a bug or an intended functionality, that FileChannel.open cannot create a file unless it is opened for write?

I'm looking at the JDK 7 Javadoc for FileChannel.open(...) .

The doc for the method says:

The READ and WRITE options determine if the file should be opened for reading and/or writing. If neither option (or the APPEND option) is contained in the array then the file is opened for reading.

The doc for CREATE_NEW says:

This option is ignored when the file is opened only for reading.

The doc for CREATE says:

This option is ignored if the CREATE_NEW option is also present or the file is opened only for reading.

Putting these three snippets together, yes, this is expected behavior.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM