06 retrieveFileStream 之后需要调用 completePendingCommand 否则业务代码会存在问题

news2024/9/25 1:17:42

前言

问题是这样的 

之前 同事碰到了这样的一个问题, 说是基于 ftp 客户端更新文件名字 更新失败 

然后 看了一下, 原来是 调用了 retrieveFileStream, 然后 没有同步等待 数据传输完成, 然后 之后直接调用了 rename 的方法 

然后 发现 rename 返回的是 false, 并且 文件名称也没有更新 

然后 这里 我们就来看一下 这个问题的相关细节 

测试用例

主要做的事情如下

1. 删除了相关的两个文件

2. 上传 test.txt

3. 获取 test.txt 的内容

4. 更新 test.txt 为 test_updated.txt 

/**
 * Test19RenameBeforeConsumeIO
 *
 * @author Jerry.X.He <970655147@qq.com>
 * @version 1.0
 * @date 2022-09-16 14:53
 */
public class Test19RenameBeforeConsumeIO {

    // Test19RenameBeforeConsumeIO
    public static void main(String[] args) throws Exception {

        FTPClient client = new FTPClient();
//        client.connect("localhost", 21);
//        client.login("root", "root");
        client.connect("192.168.0.21", 21);
        client.login("root", "root");

        client.setDataTimeout(1000 * 1000);
        client.setConnectTimeout(1000 * 1000);
        client.setSoTimeout(1000 * 1000);
        client.setDefaultTimeout(1000 * 1000);
        client.enterLocalPassiveMode();

        String targetFile = "test.txt";
        String targetFileUpdated = "test_updated.txt";
        boolean deleteFileStatus = client.deleteFile(targetFile);
        boolean deleteFileUpdatedStatus = client.deleteFile(targetFileUpdated);
        System.out.println(String.format(" delete targetFile : %s ", deleteFileStatus));
        System.out.println(String.format(" delete targetFileUpdated : %s ", deleteFileUpdatedStatus));

//        boolean storeResult = client.storeFile(targetFile, new FileInputStream("/Users/jerry/Tmp/sql02.txt"));
        boolean storeResult = client.storeFile(targetFile, new FileInputStream("/Users/jerry/Downloads/h5player.zip"));
        System.out.println(String.format(" store file : %s ", storeResult));

        System.out.println(" __passivePort : " + client.getPassivePort());
        long start01 = System.currentTimeMillis();
        InputStream is = client.retrieveFileStream(targetFile);
        long start02 = System.currentTimeMillis();
        String content = Tools.getContent(is);
//        client.completePendingCommand();

        boolean renameStatus = client.rename(targetFile, targetFileUpdated);
//        client.rnfr(targetFile);
        long start03 = System.currentTimeMillis();
//        client.rnto(targetFileUpdated);
//        client.getReply();
        long start04 = System.currentTimeMillis();
        long cost01 = start02 - start01;
        long cost02 = start03 - start02;
        long cost03 = start04 - start03;

        System.out.println(String.format(" cost1 : %s, cost2 : %s, cost3 : %s ", cost01, cost02, cost03));
        FTPFile[] childFiles = client.listFiles();
        for (FTPFile file : childFiles) {
            System.out.println(file.toFormattedString());
        }
        int x = 0;

    }

}

有这个 completePendingCommand 的情况, 是可以 正常 执行完上面一系列的流程的 

但是 没有 completePendingCommand 的话, 就回报错了 

我们这里 来看一下 具体的情况 

retrieveFileStream 的请求的流程

在执行 retrieveFileStream 之前目前 ftpClient 保存的 replyString 是 storeFile 响应的结果信息 

ftpClient 保存的 replyString 是服务器传输给客户端的将要开始进行数据传输的一个通知 

ftpClient 保存的 replyString 是服务器传输给客户端数据传输完成的一个通知 

其实 最核心的问题就是 retrieveFileStream 只发送了一次请求, 但是 服务端有两次响应 

第一次是一个 非阻塞的通知结果, 第二次是传输完成之后的一个通知结果 

所以 completePendingCommand 中的具体的实现 其实是阻塞等待 消费者第二个 传输完成之后的通知结果 

retrieveFileStream 处理的流程

我们来看一下 retrieveFileStream 的服务端的具体的处理 

第一次 非阻塞的通知结果 是在 727 行响应给客户端的 

第二次 传输完成之后的一个通知结果 是在 768 行响应给客户端的 

调试一下异常场景

我们发现 rename 之后拿到的 replyString 是 "226 Transfer complete." 

然而 这个是 retrieveFileStream 的第二个响应 

看一下 rename 里面, 原来 rename 是两条 ftp 命令组合而来的 

rnfr 是传递原文件名, rnto 是传递更新之后的文件名 

我们看一下 正常情况下 rnfr 的响应, 是一个 ”350 Ready for RNTO.“

而我们上面 拿到的是 retrieveFileStream 的第二个响应, 所以 我们只需要在 retrieveFileStream 操作完成之后, 消费掉 retrieveFileStream 的第二个响应的第二个响应就行了 

问题的处理 

在 retrieveFileStream 处理之后, 其他的请求处理之前同步消费 retrieveFileStream 的第二个响应即可 

可以直接 completePendingCommand 或者调用 getReply 

当然 completePendingCommand 会更好一些, 还有一个 状态的判断 

//        client.completePendingCommand();
        client.getReply();

异常场景 rnfr 怎么样了?

rnfr 发送给了服务器 

服务器那边 单独一个进程来处理的客户端这边的 process_post_login 来处理的各个请求, 是同步的 

因此 rnfr 命令是已经发送到了 服务器, 但是 rnfr 中 getReply 是阻塞等待在 retrieveFileStream 的传输完成, 传输完成之后 拿到 retrieveFileStream 的第二个响应 "226 Transfer complete." 

然后 后面走的 "if (!FTPReply.isPositiveIntermediate(rnfr(from))) {" 的判断 

这个命令传输过去了, 服务器 也处理了, 下一个客户端请求 listFiles 拿到的是 rfnr 的响应结果, 然后 listFiles 报错了, 效果如下 

怎么在异常场景下面更新文件名?

rename 在异常场景下面发送了 rnfr 的请求, 并且消费了 retrieveFileStream 的第二个响应 

那么这时候 只要按照 rename 的逻辑, 消费掉 rnfr 的响应, 然后再发送 rnto 的请求就可以了 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/131664.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

看我今年奋斗,观我未来之路

看我今年奋斗&#xff0c;观我未来之路 。 博客之星评选已经进行了几天。我也没有很去关注这东西&#xff0c;毕竟一个刚注册一年、写了无数水文的误导他人的博主&#xff0c;怎么可能拿到博客之星&#xff1f; 我&#xff0c;只是无聊时转发一下&#xff0c;那句毫无新意、从…

大学生如何在网上赚零花钱,适合学生党可做的零花钱项目

大学生的课余时间是非常多的&#xff0c;利用这些时间&#xff0c;我们可以去做点小兼职赚点零花钱&#xff0c;既可以补贴生活费&#xff0c;又可以获得不一样的生活体验。 现在大学生完全可以不用去大街上发传单&#xff0c;或者去咖啡店当服务员了&#xff0c;自己在网上就…

【数据结构】单链表(线性表)的实现

目录 一、什么是链表 二、单链表的实现 1、动态申请一个结点 2、单链表打印 3、单链表尾插 4、单链表的尾删 5、单链表的头插 6、单链表头删 7、单链表查找 8、单链表在pos位置之后插入x 9、单链表删除pos位置之后的值 10、单链表在pos位置之前插入x 11、单链表删除pos位置的值…

前端期末考试试题及参考答案(04)

版权声明 本文原创作者&#xff1a;谷哥的小弟作者博客地址&#xff1a;http://blog.csdn.net/lfdfhl 一、 填空题 在页面中&#xff0c; ______标签用于创建一个表单。< form>中的______属性用于指定接收并处理表单数据的服务器url地址。< form>中的______表示以…

基于springboot+Vue的疫情防控系统(程序+数据库)

大家好✌&#xff01;我是CZ淡陌。一名专注以理论为基础实战为主的技术博主&#xff0c;将再这里为大家分享优质的实战项目&#xff0c;本人在Java毕业设计领域有多年的经验&#xff0c;陆续会更新更多优质的Java实战项目&#xff0c;希望你能有所收获&#xff0c;少走一些弯路…

Linux 资源限制 setrlimit

有的时候为了避免程序毫无意义的占用CPU&#xff08;如死循环&#xff09;、过度占用内存&#xff08;如内存泄漏&#xff09;&#xff0c;我们可以限制程序使用的资源。 下面主要从两个角度限制资源&#xff1a; 限制程序累计运行时长限制可以使用的内存大小 限制资源使用到…

微信小程序 | 小程序系统API调用

&#x1f5a5;️ 微信小程序专栏&#xff1a;小程序系统API调用 &#x1f9d1;‍&#x1f4bc; 个人简介&#xff1a;一个不甘平庸的平凡人&#x1f36c; ✨ 个人主页&#xff1a;CoderHing的个人主页 &#x1f340; 格言: ☀️ 路漫漫其修远兮,吾将上下而求索☀️ &#x1f44…

Go语言进阶

一、并发 VS 并行 1: 多线程程序在一个核的CPU上运行 2: 多线程程序在多个核的CPU上运行 Go可以充分发挥多核优势&#xff0c;高效运行。 线程&#xff1a;用户态&#xff0c;轻量级线程&#xff0c;栈MB级别。 协程&#xff1a;内核态&#xff0c;线程内跑多个协程&#xff…

最短路径的java代码实现

1.最短路径定义及性质 有了加权有向图之后&#xff0c;我们立刻就能联想到实际生活中的使用场景&#xff0c;例如在一副地图中&#xff0c;找到顶点a与地点b之间的路径&#xff0c;这条路径可以是距离最短&#xff0c;也可以是时间最短&#xff0c;也可以是费用最小等&#xff…

爬虫的奇技淫巧之ajax-hook

声明 本文仅供学习参考&#xff0c;如有侵权可私信本人删除&#xff0c;请勿用于其他途径&#xff0c;违者后果自负&#xff01; 前言 随着反爬力度的不断升级&#xff0c;现在的爬虫越来越难搞了。诸如加密参数sign、signature、token。面对这种情况传统的方式可以使用自动…

Redis集群系列九 —— 集群伸缩之扩容

集群伸缩 Redis 集群提供了灵活的节点扩容和收缩方案&#xff0c;当有新节点加入时&#xff0c;需要把一部分数据迁移到新节点来达到集群的负载均衡&#xff1b;当旧节点退出时&#xff0c;需要把其上的数据迁移到其他节点上&#xff0c;确保该节点上的数据能够被正常访问。从…

ext4 extent详解3之内核源码流程讲解

本文在前两篇《ext4 extent详解1之示意图演示》和《ext4 extent详解2之内核源码详解》讲解ext4 extent 文章的基础上&#xff0c;本文从内核源码、实例演示等角度详细介绍ext4 extent B树的形成过程&#xff0c;希望看过本文的读者能理解ext4 extent B树的工作原理。 1 &#…

6.3 返回类型和return语句

文章目录无返回值函数有返回值函数值是如何被返回的不要返回局部对象的引用或指针引用返回左值列表初始化返回值主函数main的返回值递归返回数组指针声明一个返回数组指针的函数使用尾置返回类型使用decltypereturn语句终止当前正在执行的函数并将控制权返回到调用该函数的地方…

2022年终总结:点滴积累让我不再迷茫

今年是开始写作的第二年&#xff0c;如果说第一年是起步的话&#xff0c;今年就是开始有了一些小收获了&#xff0c;通过点滴积累让我知道积累的充实感&#xff0c;通过一点一点粉丝或阅读量的积累&#xff0c;增加写作的自信。 今年的收获 首先看一下今年的阅读量和粉丝量: …

cheunghonghui的【22年度总结】

cheunghonghui的【22年度总结】 好久好久没写博客了&#xff0c;看了下后台&#xff0c;上一次发表博客已经是一年半之前&#xff0c;趁着年底&#xff0c;抓紧时间写&#xff08;水&#xff09;一篇不然就要断更了。 【年度工作总结】 1、迭代了未知bug 2、修复了已知bug …

迎接2023,他真的想说“新年快乐”

&#x1f60a;你好&#xff0c;我是小航&#xff0c;一个正在变秃、变强的文艺倾年。 &#x1f514;2023年快要到来啦&#xff0c;再此祝大家诸事顺遂&#xff0c;所见所盼皆如愿。 &#x1f514;本文讲解如何使用Java演奏一首歌曲&#xff0c;一起卷起来叭&#xff01; 众所周…

Faster RCNN网络源码解读(Ⅶ) --- RPN网络代码解析(中)RegionProposalNetwork类解析

目录 一、代码作用&#xff08;rpn_function.py&#xff09; 二、代码解析 2.1 RegionProposalNetwork类 2.1.1 初始化函数__init__ 2.1.2 正向传播过程forward 2.1.3 concat_box_prediction_layers函数 2.1.4 permute_and_flatten 2.1.5 filter_proposals 2.1.6 _…

2022 许些遗憾 年终总结

目录回首过去展望未来验收 2022年任务清单 ---------------------------》 2023年 flag2023 展望回首过去 此刻&#xff0c;想想这一年&#xff0c;口罩&#xff0c;38.5℃&#xff0c;艰难时刻&#xff0c;终究在2022最后十天被确诊了“阳”&#xff0c;没有备任何药&#xff…

Linux系列——Linux操作系统安装及服务控制(1)

作者简介&#xff1a;一名在校云计算网络运维学生、每天分享网络运维的学习经验、和学习笔记。 座右铭&#xff1a;低头赶路&#xff0c;敬事如仪 个人主页&#xff1a;网络豆的主页​​​​​​ 目录 前言 一.Linux介绍 1.Linux是什么&#xff1f; 2.Linux系统的优点 …

ArcGIS基础实验操作100例--实验31纠正栅格坐标

本实验专栏参考自汤国安教授《地理信息系统基础实验操作100例》一书 实验平台&#xff1a;ArcGIS 10.6 实验数据&#xff1a;请访问实验1&#xff08;传送门&#xff09; 高级编辑篇--实验31 纠正栅格坐标 目录 一、实验背景 二、实验数据 三、实验步骤 &#xff08;1&…