没有 WRITE 选项的 FileChannel.open(path, CREATE|CREATE_NEW) 抛出 NoSuchFileException

问题描述

我有以下代码:


  @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,Long.MAX_VALUE);
    }

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

这段代码一直抛出以下异常:

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)

查看 FileChannel.open(path,option) 的 javadoc 和相关的 StandardOpenOption,没有任何文档暗示要创建文件,您还必须打开它进行写入。

唯一有效的选项:

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

我只是通过 UnixChannelFactory.newFileChannel 确定了这一点,并注意到以下几点:

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;
            }
        }

这表明,除非您指定 WRITE 选项,否则永远不会创建文件。

这是一个错误还是预期的功能,FileChannel.open 除非打开文件才能创建文件?

解决方法

我正在查看 JDK 7 Javadoc for FileChannel.open(...)

该方法的文档说:

READWRITE 选项确定是否应打开文件进行读取和/或写入。如果数组中不包含任何选项(或 APPEND 选项),则打开文件进行读取。

CREATE_NEW 的文档说:

当文件打开仅供阅读时忽略此选项。

CREATE 的文档说:

如果 CREATE_NEW 选项也存在或打开文件仅用于读取,则忽略此选项。

将这三个片段放在一起,是的,这是预期的行为。

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...