Golang实现一个批量自动化执行树莓派指令的软件(4)上传

简介

话接上篇 Golang实现一个批量自动化执行树莓派指令的软件(3)下载
, 继续实现上传

环境描述

运行环境: Windows, 基于Golang, 暂时没有使用什么不可跨平台接口, 理论上支持Linux/MacOS
目标终端:树莓派DebianOS(主要做用它测试)

实现

接口定义

type IUploader interface {/*Upload 下载的同步接口, 会堵塞执行from : 上传的路径本地路径to   : 保存的远程路径*/Upload(from, to string) error/*UploadWithCallback 下载的同步/异步接口from : 上传的路径本地路径to   : 保存的远程路径processCallback : 进度回调函数,每次上传文件的时候被调用, 返回当前上传进度信息from : 当前上传的路径本地路径to   : 当前保存的远程路径num : 上传的文件总数uploaded     : 已上传的文件数finishedCallback : 完成上传时调用background : 表示是同步执行还是异步执行*/UploadWithCallback(from, to string,process func(from, to string, num, uploaded uint),finishedCallback func(err error), background bool) error
}

接口实现

package sshutilimport ("fmt""github.com/pkg/sftp""io""os""path""time"
)var oneTimeMaxSizeToWrite = 8192 // 单次最大写文件大小func IsDir(path string) bool {info, err := os.Stat(path)if err != nil {return false}return info.IsDir()
}func IsFile(path string) bool {info, err := os.Stat(path)if err != nil {return false}return !info.IsDir()
}type uploader struct {client       *sftp.ClientuploadSize   uintuploadNumber uintuploaded     uintstarted  boolcanceled chan struct{}
}func newUploader(client *sftp.Client) (*uploader, error) {return &uploader{client: client, canceled: make(chan struct{})}, nil
}func (u *uploader) Upload(from, to string) error {return u.upload(from, to, nil, nil)
}func (u *uploader) UploadWithCallback(from, to string,process func(from, to string, num, uploaded uint),finishedCallback func(err error), background bool) error {if !background {return u.upload(from, to, process, finishedCallback)} else {go u.upload(from, to, process, finishedCallback)}return nil
}func (u *uploader) Cancel() error {if u.started {select {case u.canceled <- struct{}{}:case <-time.After(time.Second * 2): // 取消时间过长,取消失败return fmt.Errorf("time out waiting for cancel")}}return nil
}func (u *uploader) Destroy() error {err := u.Cancel()close(u.canceled)return err
}func (u *uploader) uploadFolderCount(localPath string) (needUpload, size uint, err error) {var (infos    []os.DirEntryfileInfo os.FileInfoc, s     uint)infos, err = os.ReadDir(localPath)for _, info := range infos {if info.IsDir() {c, s, err = u.uploadFolderCount(path.Join(localPath, info.Name()))if nil != err {return}needUpload += csize += scontinue}needUpload += 1fileInfo, _ = info.Info()size += uint(fileInfo.Size())}err = nilreturn
}func (u *uploader) uploadFileCount(localpath string) (uint, uint, error) {var (isExist boolisDir   bool)info, err := os.Stat(localpath)if err != nil {isExist = !os.IsNotExist(err)isDir = false} else {isExist = trueisDir = info.IsDir()}if !isExist {return 0, 0, nil}if !isDir {return 1, uint(info.Size()), nil}return u.uploadFolderCount(localpath)
}func (u *uploader) upload(localPath, remotePath string,process func(from, to string, num, uploaded uint),finishedCallback func(err error)) (err error) {whenErrorCall := func(e error) error {if nil != finishedCallback {go finishedCallback(e)}return e}u.started = truedefer func() {u.started = false}()u.uploadNumber, u.uploadSize, err = u.uploadFileCount(localPath)if nil != err {return whenErrorCall(err)}var isDir = IsDir(localPath)if isDir {return u.uploadFolder(localPath, remotePath, process, finishedCallback)}return u.uploadFile(localPath, remotePath, process, finishedCallback)
}func (u *uploader) writeFile(reader io.Reader, writer io.Writer) (err error) {var buffer = make([]byte, oneTimeMaxSizeToWrite)var n intfor {n, err = reader.Read(buffer)if n < oneTimeMaxSizeToWrite {if io.EOF == err {err = nilif n > 0 {_, err = writer.Write(buffer[0:n])if err != nil {return err}}break}}_, err = writer.Write(buffer)if err != nil {return err}}return nil
}func (u *uploader) uploadFile(localPath, remotePath string,process func(from, to string, num, uploaded uint),finishedCallback func(err error)) error {whenErrorCall := func(e error) error {if nil != finishedCallback {go finishedCallback(e)}return e}var (srcFile        *os.FiledstFile        *sftp.FileremoteFileName stringerr            error)srcFile, err = os.Open(localPath)if err != nil {return whenErrorCall(err)}defer srcFile.Close()remoteFileName = path.Join(remotePath, path.Base(localPath))dstFile, err = u.client.Create(remoteFileName)if err != nil {return whenErrorCall(err)}defer dstFile.Close()err = u.writeFile(srcFile, dstFile)if nil != err {return whenErrorCall(err)}u.uploaded += 1if nil != process {go process(localPath, remoteFileName, u.uploadNumber, u.uploaded)}return whenErrorCall(err)
}func (u *uploader) uploadFolder(localPath, remotePath string,process func(from, to string, num, uploaded uint),finishedCallback func(err error)) (err error) {whenErrorCall := func(e error) error {if nil != finishedCallback {go finishedCallback(e)}return e}err = u.client.MkdirAll(remotePath)if nil != err {return whenErrorCall(err)}localFileInfos, err := os.ReadDir(localPath)if err != nil {return whenErrorCall(err)}for _, fileInfo := range localFileInfos {localFilePath := path.Join(localPath, fileInfo.Name())select {case <-u.canceled:return whenErrorCall(fmt.Errorf("user canceled"))default:}if fileInfo.IsDir() {remoteFilePath := path.Join(remotePath, fileInfo.Name())err = u.uploadFolder(localFilePath, remoteFilePath, process, nil)if nil != err {return whenErrorCall(err)}} else {err = u.uploadFile(localFilePath, remotePath, process, nil)if nil != err {return whenErrorCall(err)}}}return whenErrorCall(err)
}

测试用例

package sshutilimport ("fmt""github.com/pkg/sftp""golang.org/x/crypto/ssh""sync""testing""time"
)type uploaderTest struct {sshClient  *ssh.ClientsftpClient *sftp.Clientuploader *uploader
}func newUploaderTest() (*uploaderTest, error) {var (err   errordTest = &uploaderTest{})config := ssh.ClientConfig{User:            "pi",                                      // 用户名Auth:            []ssh.AuthMethod{ssh.Password("a123456")}, // 密码HostKeyCallback: ssh.InsecureIgnoreHostKey(),Timeout:         10 * time.Second,}dTest.sshClient, err = ssh.Dial("tcp", "192.168.3.2:22", &config) //IP + 端口if err != nil {fmt.Print(err)return nil, err}if dTest.sftpClient, err = sftp.NewClient(dTest.sshClient); err != nil {dTest.destroy()return nil, err}dTest.uploader, err = newUploader(dTest.sftpClient)return dTest, err
}func (d *uploaderTest) destroy() {if nil != d.sftpClient {d.sftpClient.Close()d.sftpClient = nil}if nil != d.sshClient {d.sshClient.Close()d.sshClient = nil}
}func TestUploader_Upload(t *testing.T) {var dTest, err = newUploaderTest()if nil != err {fmt.Println("fail to new uploader test!")return}defer dTest.destroy()err = dTest.uploader.Upload("./download", "/home/pi/upload/")if nil != err {fmt.Println(err)}
}func TestUploader_UploadWithCallback(t *testing.T) {var dTest, err = newUploaderTest()if nil != err {fmt.Println("fail to new uploader test!")return}defer dTest.destroy()err = dTest.uploader.UploadWithCallback("./download", "/home/pi/upload1/", func(from, to string, num, uploaded uint) {fmt.Println(from, to, num, uploaded)}, func(err error) {fmt.Println("finished!!!")}, false)if nil != err {fmt.Println(err)}time.Sleep(time.Second)
}func TestUploader_UploadWithCallbackAsync(t *testing.T) {var waiter sync.WaitGroupvar dTest, err = newUploaderTest()if nil != err {fmt.Println("fail to new uploader test!")return}defer dTest.destroy()waiter.Add(1)err = dTest.uploader.UploadWithCallback("./download", "/home/pi/upload2/", func(from, to string, num, uploaded uint) {fmt.Println(from, to, num, uploaded)}, func(err error) {waiter.Done()fmt.Println("finished!!!")}, true)if nil != err {fmt.Println(err)}fmt.Println("waiting finish...")waiter.Wait()fmt.Println("had finished!")time.Sleep(time.Second)
}

代码源

https://gitee.com/grayhsu/ssh_remote_access

其他

参考

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

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

相关文章

Vue Router基础知识整理

Vue Router基础知识整理 1. 安装与使用&#xff08;Vue3&#xff09;安装使用 2. 配置路径别名和VSCode路径提示&#xff08;了解&#xff09;3. 使用查询字符串或路径传参query动态路由 与 params 4. router-link、定义别名、定义路由名称、编程式导航定义别名 aliasrouter-li…

李沐66_使用注意力机制的seq2seq——自学笔记

加入注意力 1.编码器对每次词的输出作为key和value 2.解码器RNN对上一个词的输出是query 3.注意力的输出和下一个词的词嵌入合并进入RNN 一个带有Bahdanau注意力的循环神经网络编码器-解码器模型 总结 1.seq2seq通过隐状态在编码器和解码器中传递信息 2.注意力机制可以根…

ELK技术介绍:背景、功能及应用场景全面解析

一、ELK概述 ELK是由Elasticsearch、Logstash和Kibana三个开源软件组成的日志管理解决方案&#xff0c;这一组合在近年来得到了广泛的关注和应用。ELK的出现&#xff0c;源于大数据和云计算技术的快速发展&#xff0c;以及对高效日志管理的迫切需求。 随着企业信息化程度…

【10-10-10旁观思维】项目管理必会的思维分析工具 08(送模板~)

&#x1f468;‍&#x1f4bb;&#x1f469;‍&#x1f4bb;面对一个决策或选择&#xff0c;当你犹豫不决时&#xff0c;可以想一下 ⏰10分钟后&#xff0c;自己是怎么看待自己现在的决策&#xff0c;依然保持一致亦或会后悔&#xff1b; ⏰10个月后&#xff0c;你又会如何思…

Javascript 插值搜索与二分搜索

插值搜索和二分搜索都是在有序数组中查找目标元素的算法。它们之间的核心区别在于确定中间元素的方式。 1、二分搜索&#xff08;Binary Search&#xff09;&#xff1a;二分搜索是一种通过将目标值与数组中间元素进行比较&#xff0c;然后根据比较结果缩小搜索范围的算…

Docker资源管理-数据管理

一、CPU 资源控制&#xff1a; 1.cgroups&#xff1a; cgroups&#xff0c;是一个非常强大的linux内核工具&#xff0c;他不仅可以限制被 namespace 隔离起来的资源&#xff0c; 还可以为资源设置权重、计算使用量、操控进程启停等等。 所以 cgroups&#xff08;Control grou…

深度剖析SSD掉电保护机制-1

随着固态硬盘&#xff08;Solid State Drives, SSD&#xff09;在数据中心、企业存储、个人计算设备等领域广泛应用&#xff0c;其数据安全性与可靠性成为至关重要的考量因素。其中&#xff0c;应对突发电源故障导致的数据丢失风险的掉电保护&#xff08;Power Loss Protection…

MA-Chitosan MA甲基丙烯酸修饰羧甲基壳聚糖 MA-Chitosan

MA-Chitosan MA甲基丙烯酸修饰羧甲基壳聚糖 MA-Chitosan、 【中文名称】甲基丙烯酸化羧甲基壳聚糖 【英文名称】Chitosan-MA 【结 构】 【纯 度】95%以上 【保 存】-20℃ 【规 格】10mg,500mg,1g,5g,10g 【产品特性】 Chitosan-MA&#xff08;壳聚糖-甲基丙烯酸…

Verilog基础语法——parameter、localparam与`define

Verilog基础语法——parameter、localparam与define 写在前面一、localparam二、parameter三、define写在最后 写在前面 在使用Verilog编写RTL代码时&#xff0c;如果需要定义一个常量&#xff0c;可以使用define、parameter和localparam三种进行定义与赋值。 一、localparam …

大模型都在用的:旋转位置编码

写在前面 这篇文章提到了绝对位置编码和相对位置编码&#xff0c;但是他们都有局限性&#xff0c;比如绝对位置编码不能直接表征token的相对位置关系&#xff1b;相对位置编码过于复杂&#xff0c;影响效率。于是诞生了一种用绝对位置编码的方式实现相对位置编码的编码方式——…

机器学习day1

一、人工智能三大概念 人工智能三大概念 人工智能&#xff08;AI&#xff09;、机器学习&#xff08;ML&#xff09;和深度学习&#xff08;DL&#xff09; 人工智能&#xff1a;人工智能是研究计算代理的合成和分析的领域。人工智能是使用计算机来模拟&#xff0c;而不是人类…

关于Android中的限定符

很多对于Android不了解或是刚接触Android的初学者来说&#xff0c;对于Android开发中出现的例如layout-large或者drawable-xxhdpi这样的文件夹赶到困惑&#xff0c;这这文件夹到底有什么用&#xff1f;什么时候用&#xff1f;这里简单的说一下。 其实&#xff0c;在上面例子中&…

基于OpenCV的人脸签到系统

效果图 目录文件 camerathread.h 功能实现全写在.h里了 class CameraThread : public QThread {Q_OBJECT public:CameraThread(){//打开序号为0的摄像头m_cap.open(0);if (!m_cap.isOpened()) {qDebug() << "Error: Cannot open camera";}//判断是否有文件,人脸…

iframe实现pdf预览,并使用pdf.js修改内嵌标题,解决乱码问题

项目中遇到文件预览功能,并且需要可以打印文件.下插件对于内网来说有点麻烦,正好iframe预览比较简单,且自带下载打印等功能按钮. 问题在于左上方的文件名乱码,网上找了一圈没有看到解决的,要么就是要收费要会员(ztmgs),要么直接说这东西改不了. 使用: 1.引入 PDF.js 库&…

Spring Boot集成Redisson实现延迟队列

项目场景&#xff1a; 在电商、支付等领域&#xff0c;往往会有这样的场景&#xff0c;用户下单后放弃支付了&#xff0c;那这笔订单会在指定的时间段后进行关闭操作&#xff0c;细心的你一定发现了像某宝、某东都有这样的逻辑&#xff0c;而且时间很准确&#xff0c;误差在1s内…

与AI对话:探索最佳国内可用的ChatGPT网站

与AI对话&#xff1a;探索最佳国内可用的ChatGPT网站 &#x1f310; 链接&#xff1a; GPTGod 点击可注册 &#x1f3f7;️ 标签&#xff1a; GPT-4 支持API 支持绘图 Claude &#x1f4dd; 简介&#xff1a;GPTGod 是一个功能全面的平台&#xff0c;提供GPT-4的强大功能&…

JavaEE——Spring Boot + jwt

目录 什么是Spring Boot jwt&#xff1f; 如何实现Spring Boot jwt&#xff1a; 1. 添加依赖 2、创建JWT工具类 3. 定义认证逻辑 4. 添加过滤器 5、 http请求测试 什么是Spring Boot jwt&#xff1f; Spring Boot和JWT&#xff08;JSON Web Token&#xff09;是一对常…

苍穹外卖学习

并不包含全部视频内容&#xff0c;大部分都按照操作文档来手搓代码&#xff0c;资料&#xff0c;代码都上传git。 〇、实际代码 0.1 Result封装 package com.sky.result;import lombok.Data;import java.io.Serializable;/*** 后端统一返回结果* param <T>*/ Data pub…

软考 系统架构设计师系列知识点之软件可靠性基础知识(5)

接前一篇文章&#xff1a;软考 系统架构设计师系列知识点之软件可靠性基础知识&#xff08;4&#xff09; 所属章节&#xff1a; 第9章. 软件可靠性基础知识 第1节 软件可靠性基本概念 9.1.3 可靠性目标 前文定量分析软件的可靠性时&#xff0c;使用失效强度来表示软件缺陷对…

20232937文兆宇 2023-2024-2 《网络攻防实践》实践七报告

20232937文兆宇 2023-2024-2 《网络攻防实践》实践七报告 1.实践内容 &#xff08;1&#xff09;使用Metasploit进行Linux远程渗透攻击 任务&#xff1a;使用Metasploit渗透测试软件&#xff0c;攻击Linux靶机上的Samba服务Usermap_script安全漏洞&#xff0c;获取目标Linux…