当前位置: 首页 > 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…...

shell 循环语句

一、命令补充 1. echo 命令 echo -n 表示不换行输出 echo -e 表示输出转义符 常用的转义符有&#xff1a; 选项作用\r光标移至行首&#xff0c;并且不换行\s当前shell的名称&#xff0c;如bash\t插入Tab键&#xff0c;制表符\n输出换行\f换行&#xff0c;但光标仍停留在…...

C++(1) 命名空间

文章目录 C1. C 概述2.C 相对于 C 语言的增强2.1C 第一行代码2.2 C 补充 bool 类型2.3 作用域运算符2.4 命名空间 namespace2.4.1 命名空间基本内容和开放性2.4.2 多个命名空间操作2.4.3 命名空间函数定义和实现分离2.4.4 匿名命名空间2.4.5 命名空间别名 C 1. C 概述 C 之父…...

【机组】单元模块实验的综合调试与驻机键盘和液晶显示器的使用方式

​&#x1f308;个人主页&#xff1a;Sarapines Programmer&#x1f525; 系列专栏&#xff1a;《机组 | 模块单元实验》⏰诗赋清音&#xff1a;云生高巅梦远游&#xff0c; 星光点缀碧海愁。 山川深邃情难晤&#xff0c; 剑气凌云志自修。 目录 1. 综合实验的调试 1.1 实验…...

React中实现虚拟加载滚动

前言&#xff1a;当一个页面中需要接受接口返回的全部数据进行页面渲染时间&#xff0c;如果数据量比较庞大&#xff0c;前端在渲染dom的过程中需要花费时间&#xff0c;造成页面经常出现卡顿现象。 需求&#xff1a;通过虚拟加载&#xff0c;优化页面渲染速度 缺点&#xff1a…...

vue中的Mutations

目录 一&#xff1a;介绍 二&#xff1a;例子 一&#xff1a;介绍 Vuex 中的 mutation 非常类似于事件&#xff1a; 每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。这个回调函数就是我们实际进行状态更改的函数&#xff0c;并且它会接受 sta…...

C#用 DateAndTime.DateAdd方法和DateTime.Add(TimeSpan) 方法分别添加一段时间间隔

目录 一、基本方法 1.用 DateAndTime.DateAdd方法添加一段时间间隔 2.用DateTime.Add方法添加一段时间间隔 二、实例 1.实例1&#xff1a;用 DateAndTime.DateAdd方法 2.实例2&#xff1a;用DateTime.Add方法 一、基本方法 1.用 DateAndTime.DateAdd方法添加一段时间间隔…...

四、Kotlin 表达式

1. 常量 & 变量 1.1 可读写变量&#xff08;var&#xff09; var x initValue // x 称为可读写变量注意&#xff1a;当 var 声明的变量做成员属性时&#xff0c;默认提供 setter/getter 方法。 1.2 只读变量&#xff08;val&#xff09; val x initValue // x 称为只…...

Web开发4:单元测试

在Web开发中&#xff0c;单元测试是一种重要的开发实践&#xff0c;它可以帮助我们确保代码的质量和可靠性。通过编写和运行单元测试&#xff0c;我们可以验证代码的正确性&#xff0c;减少错误和缺陷&#xff0c;并提高代码的可维护性。本文将介绍单元测试的概念、好处以及如何…...

Ubuntu 16 让ufw防火墙控制docker容器中所有端口

使用docker ps 查询docker在运行端口。 rootai-0003:~# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS …...

<蓝桥杯软件赛>零基础备赛20周--第18周--动态规划初步

报名明年4月蓝桥杯软件赛的同学们&#xff0c;如果你是大一零基础&#xff0c;目前懵懂中&#xff0c;不知该怎么办&#xff0c;可以看看本博客系列&#xff1a;备赛20周合集 20周的完整安排请点击&#xff1a;20周计划 每周发1个博客&#xff0c;共20周。 在QQ群上交流答疑&am…...

怎么截取网站视频做动图/seo优化查询

一、前言 没有最好的架构&#xff0c;只有最适合自身业务的架构。 首先我们应该确定的是大的架构方向&#xff1a;分布式 / 单应用负载均衡&#xff0c;这两种架构设计直接影响后续的网络层、缓存层、数据层、业务层的设计。笔者这两种架构的应用都接触过&#xff0c;两种架构…...

网站优化软件开发/2021年年度关键词

Given a binary tree, return all root-to-leaf paths. Given the following binary tree: 1/ \ 2 3\5All root-to-leaf paths are: [ "1->2->5", "1->3" ] 解题思路&#xff1a;这道题目应该就是单纯的二叉树遍历&#xff0c;从根节点出发…...

求人做网站的网站/广州seo优化费用

作者 | 马超责编 | 伍杏玲封图 | 视觉中国出品 | CSDN&#xff08;ID:CSDNnews&#xff09;最近CVPR2020的论文集合在GitHub火了&#xff0c;CVPR2020-Paper- Code 的项目&#xff08;https://github.com/extreme-assistant/CVPR2020-Paper-Code-Interpretation&#xff09;已获…...

网站建设沛宣/曲靖seo建站

优点和使用场景SQL Server 内存中列存储索引通过使用基于列的数据存储和基于列的查询处理来存储和管理数据。 列存储索引适合于主要执行大容量加载和只读查询的数据仓库工作负荷。 与传统面向行的存储方式相比&#xff0c;使用列存储索引存档可最多提高 10 倍查询性能&#xff…...

保定知名网站建设公司/上海专业seo服务公司

导读&#xff1a; C/C语言很多人都比较熟悉&#xff0c;这基本上是每位大学生必学的一门编程语言&#xff0c;通常还都是作为程序设计入门语言学的&#xff0c;并且课程大多安排在大一。刚上大学&#xff0c;孩子们还都很乖&#xff0c;学习也比较认真&#xff0c;用心。所以&a…...

响应式网站公司/网站运营和维护

参考文献&#xff1a;http://linux.vbird.org/linux_server/0350dns.php 文章目录1. DNS解析过程1.1 DNS解析过程中两种模式2.DNS服务器类型3.DNS服务器安装配置3.DNS服务器配置3.1相关配置文件3.2 /etc/hosts&#xff1a;全局本地域名解析配置3.3 /etc/resolv.conf 配置文件3.…...