前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >追加模式创建的FileOutputStream获取的FileChannel设置position不影响写入位置

追加模式创建的FileOutputStream获取的FileChannel设置position不影响写入位置

作者头像
johnhuster的分享
发布2022-03-29 14:52:35
6970
发布2022-03-29 14:52:35
举报
文章被收录于专栏:johnhuster

下面看段代码:

代码语言:javascript
复制
@Test
	public void append(){
		DateTimeFormatter df = DateTimeFormatter.ofPattern("HH:mm");
		try {
            //追加模式创建FileOutputStream
			FileOutputStream fos = new FileOutputStream("d:/tmp/1.txt",true);
			FileChannel fc = fos.getChannel();
			ByteBuffer buffer = ByteBuffer.wrap(df.format(LocalTime.now()).getBytes("utf-8"));
			println("A fileChannel.position ()= " + fc.position());
			println("channel size="+fc.size());
            //此处无论position设置成什么值,下面的write方法都是在文件末尾追加内容
			fc.position(0);
			println("A fileChannel.position ()= " + fc.position());
			println( "write() 1 返回值 :" + fc.write(buffer)) ;
			println ("B fileChannel .position ()= " + fc.position());
			fc.close();
			fos.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

其实position方法调用成功了,感兴趣的可以跟踪下FileChannelImpl类的position方法:

代码语言:javascript
复制
    public long position() throws IOException {
        this.ensureOpen();
        synchronized(this.positionLock) {
            long var2 = -1L;
            int var4 = -1;

            long var5;
            try {
                this.begin();
                var4 = this.threads.add();
                if (this.isOpen()) {
                    do {
                        //通过position(long newPos)方法后看下this.nd.seek(this.fd, -1L)返回的值就知道其实设置position是成功的
                        var2 = this.append ? this.nd.size(this.fd) : this.nd.seek(this.fd, -1L);
                    } while(var2 == -3L && this.isOpen());

                    var5 = IOStatus.normalize(var2);
                    return var5;
                }

                var5 = 0L;
            } finally {
                this.threads.remove(var4);
                this.end(var2 > -1L);

                assert IOStatus.check(var2);

            }

            return var5;
        }
    }

上面这一诡异现象印证了一句话“你说的很对,但是我就是不听”

省略中间环节,直接看下FileDispatcherImpl类的write方法

代码语言:javascript
复制
    int write(FileDescriptor var1, long var2, int var4) throws IOException {
        return write0(var1, var2, var4, this.append);
    }

通过名字我们可以知道write0是个native方法,继续往下看jdk源码是怎么实现write0这个方法的:

代码语言:javascript
复制
JNIEXPORT jint JNICALL
Java_sun_nio_ch_FileDispatcherImpl_write0(JNIEnv *env, jclass clazz, jobject fdo,
                                          jlong address, jint len, jboolean append)
{
    BOOL result = 0;
    DWORD written = 0;
    HANDLE h = (HANDLE)(handleval(env, fdo));

    if (h != INVALID_HANDLE_VALUE) {
        OVERLAPPED ov;
        LPOVERLAPPED lpOv;
        //针对追加模式,特殊处理,所以不管之前position设置的位置如何,如果以追加模式打开文件,
       //在windows系统都会把数据追加到文件末尾,而不是position设置的位置
        if (append == JNI_TRUE) {
            ov.Offset = (DWORD)0xFFFFFFFF;
            ov.OffsetHigh = (DWORD)0xFFFFFFFF;
            ov.hEvent = NULL;
            lpOv = &ov;
        } else {
            lpOv = NULL;
        }
        result = WriteFile(h,           /* File handle to write */
                      (LPCVOID)address, /* pointers to the buffers */
                      len,              /* number of bytes to write */
                      &written,         /* receives number of bytes written */
                      lpOv);            /* overlapped struct */
    }

    if ((h == INVALID_HANDLE_VALUE) || (result == 0)) {
        JNU_ThrowIOExceptionWithLastError(env, "Write failed");
    }

    return convertReturnVal(env, (jint)written, JNI_FALSE);
}

总结:

1、这个问题产生的原因不是position无效或者失败,而是write方法另有蹊跷!

2、如果想自由控制position,可以使用RandomAccessFile或者通过FileChannel的int write(ByteBuffer src, long position)方法来显示传入写入位置,关于FileChannelImpl类的int write(ByteBuffer src, long position)方法以及int write(ByteBuffer var1)方法

参考文章:

1、https://docs.microsoft.com/zh-cn/windows/win32/api/fileapi/nf-fileapi-writefile

2、https://www.cnblogs.com/suanguade/p/5857746.html

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020/03/26 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档