当前位置: 首页 > news >正文

解决方案 | 基于SFTP协议的文件传输断点续传Java实现方案

背景

因项目需要,我们服务每天都需要通过SFTP协议来对接上下游进行文件传输,但是对于一些大文件,在与第三方公司的服务器对接过程中很可能会因为网络问题或上下游服务器性能问题导致文件上传或者下载被中断,每次重试都需要重新对文件进行上传和下载,非常浪费带宽、服务器资源和时间,因此我们需要尽量提升文件传输效率,减少不必要的文件传输损耗。

解决思路

我们平时用一些下载软件,都有个断点续传功能,可以基于上一次已经传输的偏移量进行传输,不需要重复传输已经传输完整的数据,大大节省文件下载或者文件上传时间。

在通过SFTP进行文件传输,同样可以利用该原理进行断点续传。

文件上传原理

上传文件时,你首先需要与SFTP服务器建立一个安全会话(Session)。这需要提供用户名、密码、SFTP服务器的地址及端口。一旦会话建立,就可以打开一个SFTP通道(Channel)进行文件传输。

在处理大文件时,为了防止因网络问题导致的文件传输中断,以及减少不必要的重复传输,我们通常会采用断点续传的方式。这意味着如果文件传输在中途中断,下一次传输可以从上次结束的地方开始,而不是重新开始。

JSch库的put方法支持断点续传。通过检查远程文件的大小,你可以确定已经上传的数据量。然后,使用FileInputStream来打开本地文件,并使用skip方法跳过已上传的部分。最后,使用put方法的RESUME标志从上次中断的地方开始上传剩余的文件部分。

这种方法的好处是:

  • 节省时间:不需要重新上传已经传输过的部分。
  • 减少资源消耗:减少网络带宽的使用,特别是在网络不稳定或计费昂贵的环境中。
  • 提高可靠性:即使在传输过程中发生中断,也可以保证最终文件的完整性。

文件下载原理

下载文件的原理与上传类似。同样需要建立会话和打开SFTP通道。使用get方法从SFTP服务器下载文件。如果你需要实现断点续传下载,你需要检查本地文件的大小,以此来确定已经下载的数据量。

如果本地文件的大小小于远程文件的大小,说明下载尚未完成,你可以从本地文件的末尾开始继续下载。JSch的get方法同样支持RESUME标志,允许你指定从远程文件的某个位置开始下载。

断点续传下载的好处包括:

  • 节省时间:如果下载被中断,可以继续从中断点开始,而不是从头开始。
  • 减少资源消耗:只下载尚未接收的文件部分,节约网络带宽。
  • 提高可靠性:保证即使在网络不稳定情况下,也可以最终获取完整文件。

代码实现

这里使用了com.github.mwiede的Jsch版本,是基于Jcraft 0.1.55增加了一些新算法的支持。

<dependency><groupId>com.github.mwiede</groupId><artifactId>jsch</artifactId><version>0.2.16</version>
</dependency>

文件上传断点续传实现:

加入SftpProgressMonitor可以更好监控文件传输的进度

package com.eshare.resumablesftp;import com.jcraft.jsch.*;import java.io.*;public class SFTPResumeUpload {private static final int PORT = 22;public static void main(String[] args) {String user = "parallels";String passwd = "xxx";String host = "192.168.50.33";String localFilePath = "/Users/evan/Downloads/1080p.mp4";String remoteFilePath = "/tmp/evan/test10.mp4";try {// 设置JSchJSch jsch = new JSch();Session session = jsch.getSession(user, host, PORT);session.setPassword(passwd);// 设置配置信息java.util.Properties config = new java.util.Properties();config.put("StrictHostKeyChecking", "no");session.setConfig(config);// 连接到服务器session.connect();// 打开SFTP通道Channel channel = session.openChannel("sftp");channel.connect();ChannelSftp sftpChannel = (ChannelSftp) channel;long remoteSize = 0;// 检查远程文件是否存在SftpATTRS attrs = sftpChannel.lstat(remoteFilePath);if (!attrs.isReg()) {throw new FileNotFoundException("Remote file does not exist: " + remoteFilePath);}// 检查远程文件大小remoteSize = attrs.getSize();// 打开本地文件RandomAccessFile raf = new RandomAccessFile(localFilePath, "r");// 计算从哪里开始上传long startPos = Math.max(0, remoteSize);raf.seek(startPos);// 文件上传long totalBytes = raf.length();OutputStream os = sftpChannel.put(remoteFilePath, new MyProgressMonitor(totalBytes - remoteSize), ChannelSftp.RESUME);byte[] buffer = new byte[1024 * 1024];//1Mint bytesRead;while ((bytesRead = raf.read(buffer)) != -1) {os.write(buffer, 0, bytesRead);}os.close();raf.close();// 检查文件传输是否已经完成ÒÒif (sftpChannel.lstat(remoteFilePath).getSize() == totalBytes) {System.out.println("File upload completed successfully.");} else {System.out.println("File upload failed.");}// 关闭连接sftpChannel.exit();session.disconnect();} catch (JSchException | IOException | SftpException e) {e.printStackTrace();}}public static class MyProgressMonitor implements SftpProgressMonitor {private long totalBytes;private long transferredBytes = 0;public MyProgressMonitor(long totalBytes) {this.totalBytes = totalBytes;}@Overridepublic void init(int op, String src, String dest, long max) {System.out.println("Starting transfer: " + src + " --> " + dest);}@Overridepublic boolean count(long bytes) {transferredBytes += bytes;double percentage = (double) transferredBytes / totalBytes * 100;System.out.printf("Transferred %d of %d bytes (%.2f%%)\n", transferredBytes, totalBytes, percentage);return true;}@Overridepublic void end() {System.out.println("\nTransfer complete.");}}}

断点续传测试步骤

1.我本地放一个2.1G的测试文件

2.准备好远程目录,这里提前创建好一个测试目录在远程虚拟机/tmp/evan

3.启动程序,控制台会打印文件传输进度,文件传输到52%左右我把程序直接杀死来模拟网络中断或者传输中断的情况

4.重新启动程序,让程序自动从上一次传输的偏移量继续上传,大家可以尝试多次中断来模拟。

5.文件传输完成后,到远程目录对比文件大小,这里也可以通过文件checksum来进行对比,以下输出结果可以看到文件被成功上传。

文件下载断点续传实现

package com.eshare.resumablesftp;import com.jcraft.jsch.*;import java.io.*;
import java.math.BigInteger;
import java.nio.file.*;
import java.security.MessageDigest;public class SFTPResumeDownload {private static final int PORT = 22;public static void main(String[] args) {String user = "parallels";String passwd = "xxx";String host = "192.168.50.33";String localFilePath = "/Users/evan/Downloads/test10.mp4";String remoteFilePath = "/tmp/evan/test10.mp4";try {// 设置JSchJSch jsch = new JSch();Session session = jsch.getSession(user, host, PORT);session.setPassword(passwd);// 设置配置信息java.util.Properties config = new java.util.Properties();config.put("StrictHostKeyChecking", "no");session.setConfig(config);// 连接到服务器session.connect();// 打开SFTP通道Channel channel = session.openChannel("sftp");channel.connect();ChannelSftp sftpChannel = (ChannelSftp) channel;// 检查远程文件是否存在SftpATTRS attrs = null;try {attrs = sftpChannel.lstat(remoteFilePath);} catch (SftpException e) {if (e.id == ChannelSftp.SSH_FX_NO_SUCH_FILE) {throw new FileNotFoundException("Remote file does not exist: " + remoteFilePath);}throw e;}// 检查本地文件大小long localSize = new File(localFilePath).length();// 打开远程文件long remoteSize = attrs.getSize();// 检查文件是否正常if (localSize >= remoteSize) {throw new FileSystemAlreadyExistsException("Local file exists and please check the size: " + remoteFilePath);}/// 计算从哪里开始下载long startPos = Math.max(0, localSize);// 文件下载FileOutputStream fos = new FileOutputStream(localFilePath, true);InputStream is = sftpChannel.get(remoteFilePath, new MyProgressMonitor(remoteSize - startPos), startPos);byte[] buffer = new byte[1024 * 1024];//1Mint bytesRead;while ((bytesRead = is.read(buffer)) != -1) {fos.write(buffer, 0, bytesRead);}is.close();fos.close();// 检查文件下载是否已经完成if (new File(localFilePath).length() == remoteSize) {System.out.println("File download completed successfully.");} else {System.out.println("File download failed.");}// 关闭连接sftpChannel.exit();session.disconnect();} catch (JSchException | IOException | SftpException e) {e.printStackTrace();}}public static class MyProgressMonitor implements SftpProgressMonitor {private long totalBytes;private long transferredBytes = 0;public MyProgressMonitor(long totalBytes) {this.totalBytes = totalBytes;}@Overridepublic void init(int op, String src, String dest, long max) {System.out.println("Starting transfer: " + src + " --> " + dest);}@Overridepublic boolean count(long bytes) {transferredBytes += bytes;double percentage = (double) transferredBytes / totalBytes * 100;System.out.printf("Downloaded %d of %d bytes (%.2f%%)\n", transferredBytes, totalBytes, percentage);return true;}@Overridepublic void end() {System.out.println("\nTransfer complete.");}}
}

断点续传测试步骤

1.我远程放一个2.1G的测试文件

parallels@ubuntu-linux-22-04-desktop:/tmp/evan$ ls -lh test10.mp4 
-rw-rw-r-- 1 parallels parallels 2.1G Jan 23 11:15 test10.mp4

2.准备好本地目录,这里是我本机下载目录/Users/evan/Downloads/
3.启动程序,控制台会打印文件传输进度,文件传输到86%左右我把程序直接杀死来模拟网络中断或者传输中断的情况

4.重新启动程序,让程序自动从上一次传输的偏移量继续上传,大家可以尝试多次中断来模拟。

5.文件传输完成后,到远程目录对比文件大小,这里也可以通过文件checksum来进行对比,以下输出结果可以看到文件被成功上传。

evan@EvandeMBP Downloads % ls -lh test10.mp4 
-rw-r--r--  1 evan  staff   2.1G Jan 23 14:39 test10.mp4
evan@EvandeMBP Downloads % 

相关文章:

解决方案 | 基于SFTP协议的文件传输断点续传Java实现方案

背景 因项目需要&#xff0c;我们服务每天都需要通过SFTP协议来对接上下游进行文件传输&#xff0c;但是对于一些大文件&#xff0c;在与第三方公司的服务器对接过程中很可能会因为网络问题或上下游服务器性能问题导致文件上传或者下载被中断&#xff0c;每次重试都需要重新对…...

web前端项目-动画特效【附源码】

文章目录 一&#xff1a;赛车游戏动画HTML源码&#xff1a;JS源码&#xff1a;CSS源码&#xff1a;&#xff08;1&#xff09;normalize.css&#xff08;2&#xff09;style.css 二&#xff1a;吉普车动画演示HTML源码&#xff1a;CSS源码&#xff1a;&#xff08;1&#xff09…...

蓝桥杯备战——6.串口通讯

1.分析原理图 由上图我们可以看到串口1通过CH340接到了USB口上&#xff0c;通过串口1我们就能跟电脑进行数据交互。 另外需要注意的是STC15F是有两组高速串口的&#xff0c;而且可以切换端口。 2.配置串口 由于比赛时间紧&#xff0c;我们最好不要去现场查寄存器手册&#x…...

Redis为什么速度快:数据结构、存储及IO网络原理总结

Redis&#xff0c;作为内存数据结构存储的佼佼者&#xff0c;其高性能表现一直备受赞誉。那么&#xff0c;Redis究竟是如何实现这一点的呢&#xff1f;接下来&#xff0c;我们将更深入地探讨其背后的关键技术&#xff0c;并提供进一步的优化策略。 一、内存存储与数据结构设计…...

OSI七层模型 | TCP/IP模型 | 网络和操作系统的联系 | 网络通信的宏观流程

文章目录 1.OSI七层模型2.TCP/IP五层(或四层)模型3.网络通信的宏观流程3.1.同网段通信3.2.跨网段通信 1.OSI七层模型 在计算机通信诞生之初&#xff0c;不同的厂商都生产自己的设备&#xff0c;都有自己的网络通讯标准&#xff0c;导致了不同厂家之间各种协议不兼容&#xff0…...

Java集合总览

1.总览 Java中的集合分List、Set、Queue、Map 4种类型。 List&#xff1a;大多数实现元素可以为null&#xff0c;可重复&#xff0c;底层是数组或链表的结构&#xff0c;支持动态扩容 Set&#xff1a;大多数实现元素可以为null但只能是1个&#xff0c;不能重复&#xff0c; …...

C# 设置一个定时器函数

C#中&#xff0c;创建设置一个定时器&#xff0c;能够定时中断执行特定操作&#xff0c;可以用于发送心跳、正计时和倒计时等。 本文对C#的定时器简单封装一下&#xff0c;哎&#xff0c;以方便定时器的创建。 定义 using Timer System.Timers.Timer;class SetTimer {Timer …...

第十四届蓝桥杯省赛pythonB组题。 管道

5407. 管道 - AcWing题库 ​​​ 有一根长度为 len的横向的管道&#xff0c;该管道按照单位长度分为 len 段&#xff0c;每一段的中央有一个可开关的阀门和一个检测水流的传感器。 一开始管道是空的&#xff0c;位于 Li 的阀门会在 Si 时刻打开&#xff0c;并不断让水流入管道。…...

淘宝扭蛋机小程序:新时代的互动营销与娱乐体验

随着科技的快速发展&#xff0c;小程序已经成为人们日常生活中不可或缺的一部分。在众多的小程序中&#xff0c;淘宝扭蛋机小程序以其独特的互动性和趣味性&#xff0c;吸引了大量用户。本文将深入探讨淘宝扭蛋机小程序的特色、用户体验以及未来发展。 一、淘宝扭蛋机小程序的…...

深度强化学习(王树森)笔记02

深度强化学习&#xff08;DRL&#xff09; 本文是学习笔记&#xff0c;如有侵权&#xff0c;请联系删除。本文在ChatGPT辅助下完成。 参考链接 Deep Reinforcement Learning官方链接&#xff1a;https://github.com/wangshusen/DRL 源代码链接&#xff1a;https://github.c…...

【分布式技术专题】「分布式技术架构」 探索Tomcat技术架构设计模式的奥秘(Server和Service组件原理分析)

探索Tomcat技术架构设计模式的奥秘 Tomcat系统架构分析Tomcat 整体结构Tomcat总体结构图以 Service 作为“婚姻”1) Service 接口方法列表 2) StandardService 的类结构图方法列表 3) StandardService. SetContainer4) StandardService. addConnector 以 Server 为“居”1) Ser…...

常用的gpt-4 prompt words收集8

本文介绍我最近收集的一些好用的chatgpt-4的prompts&#xff0c;如果你也有好用的提示词可以互相交流一下。 1. I ran into some trouble on my way to work. 迟到原因 2. In my heart, the most delicious coffee is the Hawaii Dirty from Manner. Only the Nong series a…...

【GitHub项目推荐--开源2D 游戏引擎】【转载】

microStudio 是一个可在浏览器中运行的游戏引擎&#xff0c;它拥有一套精美、设计精良、全面的工具&#xff0c;可以非常轻松地帮助你创建 2D 游戏。 你可以在浏览器中访问 microStudio.dev 开始搭建你的游戏&#xff0c;当然你可以克隆现有项目或创建新游戏并开始编码&#x…...

鸿蒙APP的应用场景

鸿蒙APP可以用于多种场合和设备类型&#xff0c;这是鸿蒙系统的分布式能力和多终端适配的优势。以下是一些鸿蒙APP的应用场景&#xff0c;希望对大家有所帮助。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发公司&#xff0c;欢迎交流合作。 1.智能手机和平板电脑&am…...

goland课程管理(6)

项目目录结构如下图所示&#xff1a; core包下面&#xff1a; class.go package coreimport "github.com/gin-gonic/gin"func Class1(ctx *gin.Context) {}course.go package coreimport (. "cookie/database". "cookie/model""fmt"…...

04.Elasticsearch应用(四)

Elasticsearch应用&#xff08;四&#xff09; 1.什么是索引 索引是文档的容器&#xff0c;是一类文档的结合索引是一个逻辑命名空间&#xff0c;它映射到一个或多个主分片&#xff0c;并且可以具有零个或多个副本分片索引中数据分散在Shard上索引的Mapping定义文档字段的类型…...

Python之数据可视化(地图)

目录 一 基础地图应用 二 全国疫情图 一 数据准备 二 数据处理 二 湖北省疫情图 一 数据准备 二 数据处理 一 基础地图应用 导入map地图对象 from pyecharts.charts import Map map Map() 写入数据 data [("北京市",100),("上海市"…...

etcd技术解析:构建高可用分布式系统的利器

1. 引言 随着云原生技术的兴起&#xff0c;分布式系统的构建变得愈发重要。etcd作为一个高可用的分布式键值存储系统&#xff0c;在这个领域发挥着至关重要的作用。本文将深入探讨etcd的技术细节&#xff0c;以及如何利用它构建高可用的分布式系统。 2. etcd简介 etcd是一个开…...

Pillow图像处理:从零开始的奇妙之旅

图像处理&#xff0c;就像是一场神奇的冒险&#xff0c;让我们的照片变得更有趣、更生动。而在这个冒险的旅途中&#xff0c;Pillow就如同一位魔法师&#xff0c;为我们开启了无尽的可能性。无论你是刚刚踏入图像处理领域的小白&#xff0c;还是已经略有基础的程序员&#xff0…...

设计一个LRU(最近最少使用)缓存

约束和假设 我们正在缓存什么&#xff1f; 我们正在缓存Web Query的结果我们可以假设输入是有效的&#xff0c;还是需要对其验证&#xff1f; 假设输入是有效的我们可以假设它适应内存吗&#xff1f; 对 编码实现 class Node(object):def __init__(self, results):self.res…...

ubuntu搭建nfs服务centos挂载访问

在Ubuntu上设置NFS服务器 在Ubuntu上&#xff0c;你可以使用apt包管理器来安装NFS服务器。打开终端并运行&#xff1a; sudo apt update sudo apt install nfs-kernel-server创建共享目录 创建一个目录用于共享&#xff0c;例如/shared&#xff1a; sudo mkdir /shared sud…...

PHP和Node.js哪个更爽?

先说结论&#xff0c;rust完胜。 php&#xff1a;laravel&#xff0c;swoole&#xff0c;webman&#xff0c;最开始在苏宁的时候写了几年php&#xff0c;当时觉得php真的是世界上最好的语言&#xff0c;因为当初活在舒适圈里&#xff0c;不愿意跳出来&#xff0c;就好比当初活在…...

1688商品列表API与其他数据源的对接思路

将1688商品列表API与其他数据源对接时&#xff0c;需结合业务场景设计数据流转链路&#xff0c;重点关注数据格式兼容性、接口调用频率控制及数据一致性维护。以下是具体对接思路及关键技术点&#xff1a; 一、核心对接场景与目标 商品数据同步 场景&#xff1a;将1688商品信息…...

CentOS下的分布式内存计算Spark环境部署

一、Spark 核心架构与应用场景 1.1 分布式计算引擎的核心优势 Spark 是基于内存的分布式计算框架&#xff0c;相比 MapReduce 具有以下核心优势&#xff1a; 内存计算&#xff1a;数据可常驻内存&#xff0c;迭代计算性能提升 10-100 倍&#xff08;文档段落&#xff1a;3-79…...

如何将联系人从 iPhone 转移到 Android

从 iPhone 换到 Android 手机时&#xff0c;你可能需要保留重要的数据&#xff0c;例如通讯录。好在&#xff0c;将通讯录从 iPhone 转移到 Android 手机非常简单&#xff0c;你可以从本文中学习 6 种可靠的方法&#xff0c;确保随时保持连接&#xff0c;不错过任何信息。 第 1…...

【HTML-16】深入理解HTML中的块元素与行内元素

HTML元素根据其显示特性可以分为两大类&#xff1a;块元素(Block-level Elements)和行内元素(Inline Elements)。理解这两者的区别对于构建良好的网页布局至关重要。本文将全面解析这两种元素的特性、区别以及实际应用场景。 1. 块元素(Block-level Elements) 1.1 基本特性 …...

有限自动机到正规文法转换器v1.0

1 项目简介 这是一个功能强大的有限自动机&#xff08;Finite Automaton, FA&#xff09;到正规文法&#xff08;Regular Grammar&#xff09;转换器&#xff0c;它配备了一个直观且完整的图形用户界面&#xff0c;使用户能够轻松地进行操作和观察。该程序基于编译原理中的经典…...

华硕a豆14 Air香氛版,美学与科技的馨香融合

在快节奏的现代生活中&#xff0c;我们渴望一个能激发创想、愉悦感官的工作与生活伙伴&#xff0c;它不仅是冰冷的科技工具&#xff0c;更能触动我们内心深处的细腻情感。正是在这样的期许下&#xff0c;华硕a豆14 Air香氛版翩然而至&#xff0c;它以一种前所未有的方式&#x…...

[ACTF2020 新生赛]Include 1(php://filter伪协议)

题目 做法 启动靶机&#xff0c;点进去 点进去 查看URL&#xff0c;有 ?fileflag.php说明存在文件包含&#xff0c;原理是php://filter 协议 当它与包含函数结合时&#xff0c;php://filter流会被当作php文件执行。 用php://filter加编码&#xff0c;能让PHP把文件内容…...

【从零开始学习JVM | 第四篇】类加载器和双亲委派机制(高频面试题)

前言&#xff1a; 双亲委派机制对于面试这块来说非常重要&#xff0c;在实际开发中也是经常遇见需要打破双亲委派的需求&#xff0c;今天我们一起来探索一下什么是双亲委派机制&#xff0c;在此之前我们先介绍一下类的加载器。 目录 ​编辑 前言&#xff1a; 类加载器 1. …...