删除个文件夹,vfs2上传文件到ftp就异常553,这么不经事吗

news/2024/9/20 2:43:06/文章来源:https://www.cnblogs.com/youzhibing/p/18353753

开心一刻

今天逛街碰到街头采访,一上来就问我敏感话题

主持人:小哥哥,你单身吗

我:是啊

主持人:你找女朋友的话,是想找一个小奶猫呢,还是小野猫呢

我沉思了一下,叹气道:如果可以的话,我想找个人,而且是女人

开心一刻

上传文件

基于 commons-vfs2 实现文件到 FTP 服务器的上传,pom.xml 如下

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.qsl</groupId><artifactId>ftp-demo</artifactId><version>1.0-SNAPSHOT</version><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.7.18</version></parent><properties><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-vfs2</artifactId><version>2.7.0</version></dependency><dependency><groupId>commons-net</groupId><artifactId>commons-net</artifactId><version>3.9.0</version></dependency></dependencies>
</project>

application.yml

server:port: 8080app:ftp:host: ftp_ipuserName: ftp账号password: ftp账号的密码port: 21protocol: ftpbaseDir: 账号基础目录

FtpConfig.java

/*** FTP 配置* @author 青石路*/
@Configuration
@ConfigurationProperties(prefix = "app.ftp")
public class FtpConfig {private String host;private String userName;private String password;private Integer port;private String protocol;private String baseDir;@Bean("fptUri")public URI fptUri() throws URISyntaxException {return new URI(protocol, userName+":"+password, host,port, baseDir, null, null);}@Beanpublic FileSystemOptions fileSystemOptions() {FileSystemOptions opts = new FileSystemOptions();FtpFileSystemConfigBuilder builder = FtpFileSystemConfigBuilder.getInstance();builder.setControlEncoding(opts, "UTF-8");builder.setConnectTimeout(opts, 5000);builder.setUserDirIsRoot(opts, true);builder.setPassiveMode(opts, true);return opts;}public String getHost() {return host;}public void setHost(String host) {this.host = host;}public String getUserName() {return userName;}public void setUserName(String userName) {this.userName = userName;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}public Integer getPort() {return port;}public void setPort(Integer port) {this.port = port;}public String getProtocol() {return protocol;}public void setProtocol(String protocol) {this.protocol = protocol;}public String getBaseDir() {return baseDir;}public void setBaseDir(String baseDir) {this.baseDir = baseDir;}
}

FileUploadManager.java 完成上传

/*** 文件上传 manager* @author 青石路*/
@Component
public class FileUploadManager {private static final Logger LOGGER = LoggerFactory.getLogger(FileUploadManager.class);private static final FileSystemManager systemManager;@Autowired@Qualifier("fptUri")private URI fptUri;@Autowiredprivate FileSystemOptions fileSystemOptions;static {try {systemManager = VFS.getManager();} catch (FileSystemException e) {throw new RuntimeException(e);}}public boolean uploadFileToSftp(File file, String fileName) {try {FileObject srcObject = systemManager.resolveFile(file.getParentFile(), file.getName());FileObject destObjectDir = systemManager.resolveFile(fptUri.toString(), fileSystemOptions);FileObject destObject = systemManager.resolveFile(destObjectDir, fileName);destObject.copyFrom(srcObject, Selectors.SELECT_SELF);return true;} catch (FileSystemException e) {LOGGER.error("文件:{} 上传SFTP失败,异常:", file.getAbsoluteFile(), e);return false;}}
}

FileUploadController.java

/*** 文件上传 controller* @author 青石路*/
@RestController
@RequestMapping("file")
public class FileUploadController {private static final Logger LOGGER = LoggerFactory.getLogger(FileUploadController.class);@Autowiredprivate FileUploadManager fileUploadManager;@GetMapping("upload")public boolean upload(@RequestParam("fileName") String fileName) {long ramdomLong = ThreadLocalRandom.current().nextLong();File file = new File(System.getProperty("java.io.tmpdir") + File.separator + ramdomLong + ".txt");LOGGER.info("localFile:{}", file.getAbsoluteFile());boolean uploadResult = false;try {Files.write(file.toPath(), String.valueOf(ramdomLong).getBytes(StandardCharsets.UTF_8));uploadResult = fileUploadManager.uploadFileToSftp(file, fileName);} catch (IOException e) {throw new RuntimeException(e);} finally {try {Files.deleteIfExists(file.toPath());} catch (IOException e) {throw new RuntimeException(e);}}return uploadResult;}
}

完整代码:ftp-demo

FTP 服务器目录最初情况如下

FTP_最初情况

/idg 下没有任何目录和文件;启动后调用接口

http://localhost:8080/file/upload?fileName=hello.txt

即可完成文件的上传;fileName 参数表示上传到 FTP 服务器上的文件名

upload_ok

true 表示上传成功,FTP 服务器上即可看到 hello.txt

upload_ok_ftp

file 目录也被自动创建了,一切都是那么的顺利

上传失败:553

一个不小心把 FTP 服务器上 file 目录给删了,但内心一点都不慌,再上传一次呗,正好我也是这么干的;正当我以为会正常上传的时候,意外来了

org.apache.commons.vfs2.FileSystemException: Could not copy "file:///C:/Users/qsl/AppData/Local/Temp/6456333409667879871.txt" to "ftp://test:***@192.168.2.118/idg/file/hello.txt".at org.apache.commons.vfs2.provider.AbstractFileObject.copyFrom(AbstractFileObject.java:303)at com.qsl.manager.FileUploadManager.uploadFileToSftp(FileUploadManager.java:47)at com.qsl.web.FileUploadController.upload(FileUploadController.java:39)at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)at java.lang.reflect.Method.invoke(Method.java:498)at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205)at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:150)at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:117)at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895)at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808)at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1072)at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:965)at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898)at javax.servlet.http.HttpServlet.service(HttpServlet.java:529)at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)at javax.servlet.http.HttpServlet.service(HttpServlet.java:623)at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:209)at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153)at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51)at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178)at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153)at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178)at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153)at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178)at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153)at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178)at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153)at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:168)at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90)at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:481)at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:130)at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93)at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:390)at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63)at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:928)at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1794)at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52)at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)at java.lang.Thread.run(Thread.java:745)
Caused by: org.apache.commons.vfs2.FileSystemException: Could not write to "ftp://test:***@192.168.2.118/idg/file/hello.txt".at org.apache.commons.vfs2.provider.AbstractFileObject.getOutputStream(AbstractFileObject.java:1280)at org.apache.commons.vfs2.provider.DefaultFileContent.buildOutputStream(DefaultFileContent.java:540)at org.apache.commons.vfs2.provider.DefaultFileContent.getOutputStream(DefaultFileContent.java:406)at org.apache.commons.vfs2.provider.DefaultFileContent.getOutputStream(DefaultFileContent.java:394)at org.apache.commons.vfs2.provider.DefaultFileContent.write(DefaultFileContent.java:815)at org.apache.commons.vfs2.provider.DefaultFileContent.write(DefaultFileContent.java:830)at org.apache.commons.vfs2.util.FileObjectUtils.writeContent(FileObjectUtils.java:203)at org.apache.commons.vfs2.provider.AbstractFileObject.copyFrom(AbstractFileObject.java:298)... 52 common frames omitted
Caused by: org.apache.commons.vfs2.FileSystemException: Cant open output connection for file "ftp://test:***@192.168.2.118/idg/file/hello.txt". Reason: "553 Can't open that file: No such file or directory
".at org.apache.commons.vfs2.FileSystemException.requireNonNull(FileSystemException.java:87)at org.apache.commons.vfs2.provider.ftp.FtpFileObject.doGetOutputStream(FtpFileObject.java:519)at org.apache.commons.vfs2.provider.AbstractFileObject.getOutputStream(AbstractFileObject.java:1276)... 59 common frames omitted

553 Can't open that file: No such file or directory 是什么鬼?莫非是 file 目录不存在的原因?试着手动创建 file 目录,再调用接口上传文件,文件正常上传!

我们来分析下,最初的时候 file 目录是不存在的,但自动创建了,文件也正常上传了,然后我们手动删除 file 目录后,上传文件失败,手动补上 file 目录后,上传又正常了,这说明 file 目录被缓存了呀,对不对?最初的时候,缓存是空的,第一次上传的时候,vfs2 会判断 FTP 服务器上是否存在 file 目录,不存在则创建并进行缓存,那么下次上传的时候,在缓存中找到了 file 目录,那么就直接上传文件了,而不用去判断 FTP 服务器上是否有 file 目录(没有则创建);缓存的作用就很明显了,减少了一次目录是否存在的网络请求,进而提高效率;当然这只是我们的猜想,是否真的存在缓存,看源码肯定是最直观的,入口代码

FileObject destObjectDir = systemManager.resolveFile(fptUri.toString(), fileSystemOptions);

就不带你们详细去跟源码了,debug 跟源码,我相信你们也很容易找到 AbstractFileSystem#resolveFile(final FileName name, final boolean useCache)

private synchronized FileObject resolveFile(final FileName name, final boolean useCache)throws FileSystemException {if (!rootName.getRootURI().equals(name.getRootURI())) {throw new FileSystemException("vfs.provider/mismatched-fs-for-name.error", name, rootName,name.getRootURI());}// imario@apache.org ==> use getFileFromCacheFileObject file;if (useCache) {file = getFileFromCache(name);} else {file = null;}if (file == null) {try {file = createFile((AbstractFileName) name);} catch (final Exception e) {throw new FileSystemException("vfs.provider/resolve-file.error", name, e);}file = decorateFileObject(file);// imario@apache.org ==> use putFileToCacheif (useCache) {putFileToCache(file);}}/*** resync the file information if requested*/if (getFileSystemManager().getCacheStrategy().equals(CacheStrategy.ON_RESOLVE)) {file.refresh();}return file;
}

如何修复

首先我们讨论下:要不要修?

手动误删目录,这种情况是非常少的,就拿我们的生产来讲,2020 到现在,从未出现过该问题,如果因为这种极小概率的事件去放弃缓存带来的性能提升,得不偿失,所以我是不推荐修改的,而实际上经过讨论后也决定不去修改;不过话说回来,如果上传压力很小,网络又非常稳定、快速,也就说缓存带来的性能提升可以忽略,那这个时候高可用的优先级就更高了,此时就可以考虑修了,那怎么修了,我这里提供两种方案

  1. 禁用缓存

    既然 vfs2 有缓存(默认是启用的),应该有开关来禁用它,我就不给你们打哑谜了,直接修改 FileSystemManager,换成其子类 StandardFileSystemManager

    private static final StandardFileSystemManager systemManager;static {try {systemManager = new StandardFileSystemManager();// 禁用缓存systemManager.setFilesCache(new NullFilesCache());systemManager.init();} catch (FileSystemException e) {throw new RuntimeException(e);}
    }
    
    禁用cache_代码前后区别

    你们也许会问,我是怎么知道可以这么调整的,你们跟一下 VFS.getManager() 就知道了,最终会看到

    // managerClassName: org.apache.commons.vfs2.impl.StandardFileSystemManager
    private static FileSystemManager createFileSystemManager(final String managerClassName) throws FileSystemException {try {// Create instancefinal Class<?> mgrClass = Class.forName(managerClassName);final FileSystemManager mgr = (FileSystemManager) mgrClass.newInstance();try {// Initializefinal Method initMethod = mgrClass.getMethod("init", (Class[]) null);initMethod.invoke(mgr, (Object[]) null);} catch (final NoSuchMethodException ignored) {/* Ignore; don't initialize. */}return mgr;} catch (final InvocationTargetException e) {throw new FileSystemException("vfs/create-manager.error", managerClassName, e.getTargetException());} catch (final Exception e) {throw new FileSystemException("vfs/create-manager.error", managerClassName, e);}
    }
    

    通过反射调用了 StandardFileSystemManager 的构造方法和 init 方法,与我们的

    systemManager = new StandardFileSystemManager();
    systemManager.init();
    

    是不是有异曲同工之妙?(你们猜的没错,我们的实现正是抄自于 vfs2

    有点东西
  2. 异常弥补

    不禁用缓存,还是保留默认的开启,只是当异常的时候,捕获它,然后去创建目录,然后再上传一次

    public boolean uploadFileToSftp(File file, String fileName, boolean isRetry) {String destDir = fptUri.toString();try {FileObject srcObject = systemManager.resolveFile(file.getParentFile(), file.getName());FileObject destObjectDir = systemManager.resolveFile(destDir, fileSystemOptions);FileObject destObject = systemManager.resolveFile(destObjectDir, fileName);destObject.copyFrom(srcObject, Selectors.SELECT_SELF);return true;} catch (FileSystemException e) {if (isRetry) {try {LOGGER.info("创建目录{}开始", destDir);FileObject destObjectDir = systemManager.resolveFile(destDir, fileSystemOptions);destObjectDir.createFolder();LOGGER.info("创建目录{}开始", destDir);} catch (FileSystemException ex) {LOGGER.error("创建目录失败,异常:", ex);}return uploadFileToSftp(file, fileName, false);}LOGGER.error("文件:{} 上传SFTP失败,异常:", file.getAbsoluteFile(), e);return false;}
    }
    
    异常弥补_代码前后区别

    既保留了缓存,也解决了目录误删的问题,就问你们服不服?

    愣着干啥,鼓掌

总结

vfs2 是有缓存的,如果不小心把 FTP 目录删除了,上传会失败并提示

553 Can't open that file: No such file or directory

可以通过手动补目录的方式就行处理,当然也可以通过重启服务来解决,但这两种都不是通过代码来解决的,可用性很低;通过代码的方式来解决,有两种方法

  1. 禁用 vfs2 缓存,但会降低性能,可用但不推荐

  2. 异常弥补,既保留了缓存,也解决了目录误删的问题,可以用也推荐

    异常捕获后用来做流程控制,条件控制,不太规范

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

8.13信息学集训_数据结构

目录P1981 [NOIP2013 普及组] 表达式求值P1449 后缀表达式P5788 【模板】单调栈P1886 滑动窗口 /【模板】单调队列P1901 发射站P1540 [NOIP2010 提高组] 机器翻译P2085 最小函数值P6033 [NOIP2004 提高组] 合并果子 加强版P1631 序列合并P2251 质量检测P1305 新二叉树B3631 单向…

Windows Server 2025 中文版、英文版下载 (Inside Preview, updated Aug 2024) - 下一代 Windows 11 Server

Windows Server 2025 中文版、英文版下载 (Inside Preview, updated Aug 2024) - 下一代 Windows 11 ServerWindows Server 2025 中文版、英文版下载 (Inside Preview, updated Aug 2024) - 下一代 Windows 11 Server Windows Server 2025 正式版发布在即 请访问原文链接:http…

如何让您的 .NET应用程序更智能-- 请参加 8.20 的 .NET Conf--Focus on AI

Microsoft 将于 2024 年 8 月 20 日举办免费的 .NET Conf: Focus on AI。该虚拟活动为开发人员提供了如何集成 .NET 和 AI 以增强应用程序开发和用户体验的见解,其中包括专家和行业领导者的会议。 为什么你不应该错过这个活动? .NET Conf: Focus 系列由全年举行的小型现场活…

如何让您的 .NET应用程序更智能-- 请参加 8.20 的 .NET Conf ndash; Focus on AI

Microsoft 将于 2024 年 8 月 20 日举办免费的 .NET Conf: Focus on AI。该虚拟活动为开发人员提供了如何集成 .NET 和 AI 以增强应用程序开发和用户体验的见解,其中包括专家和行业领导者的会议。为什么你不应该错过这个活动?.NET Conf: Focus 系列由全年举行的小型现场活动…

如何让您的 .NET应用程序更智能-- 请参加 8.20 的 .NET Conf -- Focus on AI

Microsoft 将于 2024 年 8 月 20 日举办免费的 .NET Conf: Focus on AI。该虚拟活动为开发人员提供了如何集成 .NET 和 AI 以增强应用程序开发和用户体验的见解,其中包括专家和行业领导者的会议。为什么要参加?.NET Conf: Focus 系列由全年举行的小型现场活动组成。8 月 20…

读零信任网络:在不可信网络中构建安全系统17无控制器架构

无控制器架构1. 建立系统框图 1.1. 实现零信任网络的第一步重要工作是建立系统框图 1.2. 系统框图能够帮助我们透彻地理解内部网络和外部网络间的通信模式,有助于系统通信信道的设计 1.3. 对于现有的网络来说,建议首先利用日志工具来记录网络流量,然后观察通信信息的流向,一…

计算机体系结构技术杂谈(下)

计算机体系结构技术杂谈(下) 2.8 加速Transformer:稀疏注意力加速器分析 1. 稀疏注意力加速器简介 近年来, Transformer模型在深度学习的各个领域,包括自然语言处理、图像分类、图像及语音生成等方面,都取得了远超于传统神经网络模型的表现。最近的ChatGPT和各类基于Tran…

计算机体系结构技术杂谈(中)

计算机体系结构技术杂谈(中) 例2:进阶算法 基础算法无法解决中断恢复的问题,即假如有两个写寄存器的操作,指令1,指令2,可能乱序执行时指令2的结果已经将写回了寄存器,但是指令1还未执行,此时发生中断后,从指令1重新开始执行,就会重新进行两次写入,将会发生错误。 只…

算子计算与调度杂谈

算子计算与调度杂谈 9.4.1 GPU内核驱动分析概述 不同CPU相比,GPU中包含了大量的并行计算单元,适合处理像素,矩阵,坐标等大量同类型的数据,因此,很多LINUX上的应用程序为了能够利用GPU的加速功能,都试图和GPU直接打交道,因此,系统中可能有多个组件或者程序同时使用GPU,…

misc一题

题目一开始给了个docx文件 无法打开 010editor发现为pk开头 后缀名改为zip 翻目录在1/page路径下找到使用notepad++查看 在unicodestring字段后进行拼接得到flag flag{xps?Oh,Go0d}

C#项目—模拟考试

C#模拟考试软件 开发了一个《模拟考试》的小软件,此小软件练习的目的主要是为了体会编程思想,深度理解高内聚、低耦合,掌握编程思维逻辑的大招,告别垃圾代码,重点体会编程之美,练习时长30分钟;开发一个项目之前,切记不要打开程序就写代码,首先要做的就是分析项目,从项…

基于直流潮流的IEEE30电力系统停电分布及自组织临界性分析matlab仿真

1.课题概述 详细的讲,我们的这个算法的安如下的步骤进行:步骤1:k=k+1,通过慢动态过程中的几个公式,对Pmax,Fmax进行更新;步骤2:考虑随机因素进行线路的断开,以一个随机概率来随机断开一条支路;步骤3:根据慢动态计算得到的参数开始进行慢动态仿真;步骤4:在慢动态仿…