C#通过Renci.SshNet或者Tamir.SharpSsh进行SFTP上传

2 篇文章 1 订阅
1 篇文章 0 订阅

C# WinForm通过Renci.SshNet和Tamir.SharpSsh进行SFTP文件上传

最近在做WinForm的SFTP文件上传,通过自己的摸索和网上的查询,发现有两个DLL可以使用,但是两个都有缺点,不太满足我的要求,代码里再详细说明,把研究的内容和大家分享一下,同时也记录一下最近学习的东西。
本篇包含两方面内容,
一、通过Renci.SshNet进行SFTP上传
二、通过Tamir.SharpSsh进行SFTP上传

Renci.SshNet百度云下载 提取码:dpif
Tamir.SharpSsh百度云下载 提取码:65y6

一、使用Renci.SshNet文件上传
以下只写关键代码,很多跟项目相关的省略掉了,根据自己的需要修改,项目中用到了多线程上传,这里以单线程上传为例。
UploadFile1方法采用读取和写入远程文件流的方式实现上传,优点是支持断点续传,缺点是速度慢
UploadFile2方法采用SftpClient的UploadFile方法,优点是速度快,缺点是不支持断点续传,哪位网友知道怎么续传的话麻烦告知一下

//添加引用
using System.Threading;
using Renci.SshNet;
using Renci.SshNet.Sftp;

//上传进度委托
public delegate void DelUploadProgress(string fileId, string fileName, double progress);
//上传完成委托
public delegate void DelUploadComplete(string fileId, UploadStatus status);
//委托对应的事件
public event DelUploadProgress UploadProgressEvent;
public event DelUploadComplete UploadCompleteEvent;

/// <summary>
/// 通过读取远程文件的FileStream实现断点续传功能,变量m_running控制上传和停止
/// 缺点就是上传速度贼慢,本机测试正常上传速度是2M左右,用这种方法只有300K左右,
/// 参考下面的UploadFile2方法,可以实现峰值传输,缺点就是无法实现断点续传(如果有网友知道怎么实现断点续传,望告知)
/// </summary>
private void UploadFile1(UploadFileInfo m_fileInfo)
{
	string remotePath = "/Files/" + m_fileInfo.FileName; //要上传到的远程文件
	SftpClient m_sftp = new SftpClient("你的服务器IP", 22, "UserName", "Password");
	m_sftp.SendKeepAlive();
	m_sftp.BufferSize = 1024 * 1024 * 10;
	m_sftp.OperationTimeout = new TimeSpan(0, 0, 5); //超时设置5秒
	SftpFileStream sfs = null; //远程FileStream
	FileStream fs = null; //本地FileStream
	try
	{
		double progress = 0; //上传进度
		m_sftp.Connect(); //连接sftp服务器
		//直接用FileMode.Append追加文件时如果文件不存在会报文件不存在的错误,先创建一个空文件再追加
		SftpFileStream tempStream = m_sftp.Open(remotePath, FileMode.OpenOrCreate, FileAccess.Read);
		tempStream.Close();
		
		//远程文件FileStream
		sfs = m_sftp.Open(remotePath, FileMode.Append, FileAccess.ReadWrite); 
		long hasUpSize = sfs.Length; //已上传大小
		
		fs = File.OpenRead(m_fileInfo.LocalPath); //本地文件FileStream
		fs.Seek(hasUpSize, SeekOrigin.Current);

		byte[] readByte = new byte[1024]; //一次读取1024字节
		int readSize = fs.Read(readByte, 0, readByte.Length);
		hasUpSize += readSize;
		progress = (hasUpSize * 1.0 / m_fileInfo.FSize) * 100;
		m_fileInfo.Progress = progress;
		if (readSize == 0 || progress >= 100) //这里判断防止用户上传到一半暂停,后续把源文件替换成别的文件的情况
		{
			progress = 100;
			m_fileInfo.Progress = 100;
		}
		while (readSize > 0)
		{
			if (!m_running) //m_running标识是否在运行 bool类型
			{
				m_fileInfo.Status = UploadStatus.Pause;
				m_fileInfo.State = "已暂停";
				break;
			}
			sfs.Write(readByte, 0, readSize);
			if (hasUpSize >= m_fileInfo.FSize)
			{
				progress = 100;
				m_fileInfo.Progress = progress;
				break;
			}
			else
			{
				readSize = fs.Read(readByte, 0, readByte.Length);
				hasUpSize += readSize;
			}

			progress = (hasUpSize * 1.0 / m_fileInfo.FSize) * 100;
			if (progress - m_fileInfo.Progress >= 0.5 || progress == 100)
			{
				m_fileInfo.Progress = progress;
				UploadProgressEvent?.Invoke(m_fileInfo.id, m_fileInfo.FileName, progress);
			}
		}

		fs.Close();
		sfs.Close();

		if (m_running || progress == 100)
		{
			m_running = false;
			m_fileInfo.Status = UploadStatus.Complete;
			m_fileInfo.State = "上传完成";
			UploadCompleteEvent?.Invoke(m_fileInfo.id, UploadStatus.Complete);
		}
	}
	catch (Exception ex)
	{
		m_running = false; 
		m_fileInfo.Status = UploadStatus.Error;
		m_fileInfo.State = "上传失败," + ex.Message;
		UploadCompleteEvent?.Invoke(m_fileInfo.id, UploadStatus.Error);
	}
	finally
	{
		if (fs != null) fs.Close();
		//if (sfs != null) sfs.Close(); //断网时这里会报错 所以注释掉了
		Disconnect();
	}
}

/// <summary>
/// 关闭连接
/// </summary>
private void Disconnect()
{
	try
	{
		if (m_sftp != null && m_sftp.IsConnected)
		{
			m_sftp.Dispose(); //这里为什么不用Disconnect方法呢,因为断网时Disconnect会卡一会,而Dispose不会
		}
	}
	catch (Exception)
	{
	}
}

/// <summary>
/// 直接调用SftpClient的UuploadFile方法,优点就是速度贼快,能达到理论峰值速度,
/// 缺点就是不支持断点续传,每次都会覆盖文件(如果有网友知道怎么实现断点续传,望告知)
/// </summary>
private void UploadFile2()
{
	string filePath = @"D:\00000FileDownload\aaa\KODExplorer.zip";
	FileInfo fi = new FileInfo(filePath);
	string remotePath = "/Files/" + fi.Name; //要上传到的远程文件
	SftpClient m_sftp = new SftpClient("服务器IP", 22, "用户名", "密码");
	m_sftp.SendKeepAlive();
	//m_sftp.BufferSize = 1024 * 1024 * 10;
	//m_sftp.OperationTimeout = new TimeSpan(0, 0, 5); 
	FileStream fs = null; //本地FileStream
	try
	{
		m_sftp.Connect(); //连接sftp服务器 

		fs = System.IO.File.OpenRead(filePath); 
		 
		m_sftp.UploadFile(fs, remotePath, callBackFun);

		fs.Close(); 
	}
	catch (Exception ex)
	{
		MessageBox.Show(ex.Message);
	}
}

/// <summary>
/// 上传回调函数(猜测:方法设置成bool类型,返回false的时候是不是就停止传输了,没有尝试,有兴趣的同学可以试一下,下面的Tamir.SharpSsh就是用这种方式实现停止的)
/// </summary>
/// <param name="size">已上传大小</param>
private void callBackFun(ulong size)
{
}

二、使用Tamir.SharpSsh文件上传
Tamir.SharpSsh通过继承SftpProgressMonitor抽象类,重写count方法,可以很好地控制上传进度,不管是直接上传文件还是上传流上传速度都在1M左右,但是有个比较大的问题就是多线程上传时经常报错,不知道为啥

using Tamir.SharpSsh.java.io;
using Tamir.SharpSsh.java.util;
using Tamir.SharpSsh.jsch;

//登录验证信息       
public class MyUserInfo : UserInfo
{
	String passwd;
	public String getPassword() { return passwd; }
	public void setPassword(String passwd) { this.passwd = passwd; }

	public String getPassphrase() { return null; }
	public bool promptPassphrase(String message) { return true; }

	public bool promptPassword(String message) { return true; }
	public bool promptYesNo(String message) { return true; }
	public void showMessage(String message) { Console.WriteLine(message); }

}

private void UploadFile(object fileName)
{
	try
	{
		string filePath = fileName.ToString();

		JSch jsch = new JSch();
		Tamir.SharpSsh.jsch.Session m_session = jsch.getSession("SFTP用户名", "IP", 22);
		MyUserInfo ui = new MyUserInfo();
		ui.setPassword("SFTP用户密码");
		m_session.setUserInfo(ui);
		m_session.connect();
		Channel m_channel = m_session.openChannel("sftp");
		m_channel.connect();
		ChannelSftp m_sftp = (ChannelSftp)m_channel;

		//string filePath = @"D:\00000FileDownload\aaa\KODExplorer.zip";
		FileInfo fi = new FileInfo(filePath);
		string remotePath = "/Files/" + fi.Name; //要上传到的远程文件

		InputStream ins = new FileInputStream(filePath);
		long len = 0;

		//这种方式取Length属性居然获取不到文件大小
		//OutputStream outs = m_sftp.put(remotePath); 
		//outs.close();

		//遍历远程目录 通过这种方式获取已上传文件大小,前提是文件存在,否则报错
		//Vector vec = m_sftp.ls(remotePath);
		//if (vec.size() > 0)
		//{
		//    SftpATTRS sattr = (vec[0] as Tamir.SharpSsh.jsch.ChannelSftp.LsEntry).getAttrs();
		//    len = sattr.getSize();
		//}

		DateTime dt1 = DateTime.Now;

		ins.Seek(len, SeekOrigin.Begin);
		m_sftp.put(ins, remotePath, new MyMonitorProgress(), ChannelSftp.APPEND);
		ins.close();

		//流的方式上传
		//OutputStream outs = m_sftp.put(remotePath);
		//byte[] readByte = new byte[1024 * 10];
		//int readSize = ins.Read(readByte, 0, readByte.Length);
		//while (readSize > 0)
		//{
		//    outs.Write(readByte, 0, readSize);
		//    //outs.Flush();
		//    readSize = ins.Read(readByte, 0, readByte.Length);
		//}
		//ins.Close();
		//outs.Close();

		DateTime dt2 = DateTime.Now;

		MessageBox.Show((dt2 - dt1).Seconds.ToString());

		m_sftp.quit();

		m_channel.disconnect();

		m_session.disconnect();
	}
	catch (Exception ex)
	{
		MessageBox.Show(ex.Message);
	}
}

/// <summary>
/// 上传进度控制类
/// </summary>
public class MyMonitorProgress : SftpProgressMonitor
{
	/// <summary>
	/// 返回false时停止上传
	/// </summary>
	/// <param name="count">一次上传的文件块大小</param>
	/// <returns></returns>
	public override bool count(long count)
	{

		return true;
	}

	/// <summary>
	/// 上传结束调用
	/// </summary>
	public override void end()
	{

	}

	/// <summary>
	/// 网上说上传开始时调用该方法,我这加了断点也没走,先不管了
	/// </summary>
	/// <param name="op">应该是一个标识字段</param>
	/// <param name="src">源文件</param>
	/// <param name="dest">目标路径</param>
	/// <param name="max">文件总大小</param>
	public override void init(int op, string src, string dest, long max)
	{

	}
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值