运维开发网

集成C#通过界面操作实现下载文件的全过程

运维开发网 https://www.qedev.com 2022-05-21 18:27 出处:网络
使用脚本进行下载的需求很常见,下面这篇文章主要给大家介绍了关于Python集成C#实现界面操作下载文件功能的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下

使用脚本进行下载的需求很常见,下面这篇文章主要给大家介绍了关于Python集成C#实现界面操作下载文件功能的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下


〇、写在前面

你说得对。Python和C#其实可以单独实现我们想要实现的功能。在这里,笔者只是试图以实验和学习的态度来解决问题。

我是一名C#程序员,目前正在学习Python。我自然要欣赏Python独特的glue语言特性,于是有了这篇文章。

如果你学会了用Python调用C#,你可以做很多你想都想不到的事情。很有趣,不是吗?


一、这个功能是怎么样的

我熟悉使用C # ampWinForm开发接口,现在刚学会Python的网络编程基础库socket,就想到了写程序。想法如下:

程序运行时会打开一个 WinForm 窗体,窗体上有:输入文件下载地址的地址栏选择文件保存位置的文件开窗按钮当前下载状态的状态区域下载按钮输入下载地址,选择一个文件保存位置点击下载按钮下载文件,状态区域显示文件下载状态,最好能显示下载进度界面放到 WinForm,下载功能放到 Python


二、WinForm 端功能实现

WinForm分为几个函数。

界面设计提供下载地址的公共属性提供文件存储地址公共属性提供用于委托下载事件的委托定义提供记录状态信息的公共方法提供更新进度信息的公共方法


1. 界面设计

首先,我们使用VS创建一个类库项目


至于为什么。NET 5或。不使用net core,是因为Python调用C#动态链接库。


创建项目后创建新表单。


在本例中,设计界面设计如下:



2. 方法定义/// lt;summarygt;/// 当前地址/// lt;/summarygt;public string ThisUrl{ get { return textUrl.Text; }}/// lt;summarygt;/// 当前保存路径/// lt;/summarygt;public string ThisSavePath{ get { return textSavePath.Text; }}/// lt;summarygt;/// 下载事件委托/// lt;/summarygt;public event EventHandler DownloadEvent;/// lt;summarygt;/// 下载按钮事件/// lt;/summarygt;/// lt;param name="sender"gt;lt;/paramgt;/// lt;param name="e"gt;lt;/paramgt;private void buttonDownload_Click(object sender, EventArgs e){ if (string.IsNullOrEmpty(this.textUrl.Text)) { MessageBox.Show("请先输入要下载的文件地址!"); this.textUrl.Focus(); return; } if (string.IsNullOrEmpty(this.textSavePath.Text)) { MessageBox.Show("请先选择文件要保存的地址!"); this.textSavePath.Focus(); return; } // 调用委托事件 if(this.DownloadEvent != null) { this.DownloadEvent.Invoke(this, e); }}

当打开保存文件的路径时,将会报告一个错误。

在调用OLE之前,当前线程必须设置为单线程单元(STA)模式。请确保您的主函数标记有STAThreadAttribute

很无奈,因为我们的调用者不是C#的主函数,目前也不知道Python是怎么调用C#的,所以只能想另一个办法,就是单独打开一个线程开发选择路径保存文件的窗口,在子线程上标记STA。

/// 选择按钮事件/// lt;/summarygt;/// lt;param name="sender"gt;lt;/paramgt;/// lt;param name="e"gt;lt;/paramgt;private void buttonSelect_Click(object sender, EventArgs e){ if (string.IsNullOrEmpty(this.textUrl.Text)) { MessageBox.Show("请先输入要下载的文件地址!"); this.textUrl.Focus(); return; } var index = this.textUrl.Text.LastIndexOf("/"); var fileName = this.textUrl.Text.Substring(index + 1); Thread importThread = new Thread(() =gt; { var text = OpenDialog(fileName); MessageEvent(text); }); importThread.SetApartmentState(ApartmentState.STA); //重点 importThread.Start();}/// lt;summarygt;/// 打开对话框/// lt;/summarygt;private string OpenDialog(string fileName){ var saveFileDialog = new SaveFileDialog(); saveFileDialog.Filter = "所有文件 (*.*)|*.*"; saveFileDialog.FilterIndex = 0; if (!string.IsNullOrEmpty(fileName)) { saveFileDialog.FileName = Path.Combine(saveFileDialog.FileName, fileName); } DialogResult dialogResult = saveFileDialog.ShowDialog(); if (dialogResult == DialogResult.OK) { return saveFileDialog.FileName; } return String.Empty;}


三、Python 端功能实现

Python有几个函数。

程序调用 .NET 类库打开窗体程序中存在下载指定 URL 文件存储到指定路径的函数定义程序结束的函数定义把当前程序封装成可运行程序(如:Windows 中为封装成 exe)import socketimport timeimport remainapp = None# 调用动态链接库的更新状态信息def LogInfo(text): # print(text) mainapp.LogInfo(text) # 调用动态链接库的更新下载进度def downloadInfo(c, all): mainapp.SetProcess(c, all) if c == all: # LogInfo("下载进度 {:.2f}".format(c / all * 100)) LogInfo("下载完成。") # else: # LogInfo("下载进度 {:.2f}%".format(c / all * 100))# 监听下载委托事件def Download(source, args): thisurl = source.ThisUrl.lower() thispath = source.ThisSavePath LogInfo("下载地址是: {}".format(thisurl)) LogInfo("保存路径为: {}".format(thispath)) reobj = re.compile(r"""(?xi)\A [a-z][a-z0-9+\-.]*:// # Scheme ([a-z0-9\-._~%!$amp;'()*+,;=]+@)? # User ([a-z0-9\-._~%]+ # Named or IPv4 host |\[[a-z0-9\-._~%!$amp;'()*+,;=:]+\]) # IPv6+ host """) match = reobj.search(thisurl) if match: HOST = match.group(2) PORT = 443 if thisurl.startswith('https') else 80 mysock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) mysock.connect((HOST, PORT)) urlend = 'GET {} HTTP/1.0\r\n\r\n'.format(thisurl).encode() # LogInfo("传递参数: {}".format(urlend)) LogInfo("开始下载……") mysock.sendall(urlend) count = 0 picture = b"" hearlength = 0 filelength = 0 hearc = b"" while True: data = mysock.recv(5120) if (len(data) lt; 1): break time.sleep(0.1) count = count + len(data) picture = picture + data # print(len(data), count) if hearlength == 0: hearlength = picture.find(b"\r\n\r\n") if hearlength gt; 0: hearc = picture[:hearlength].decode() # print(hearc) sear = re.search('Content-Length: ([0-9]+)', hearc) if sear: filelength = int(sear.groups()[0]) downloadInfo(count - 4 - hearlength, filelength) else: downloadInfo(count - 4 - hearlength, filelength) mysock.close() # Skip past the header and save the picture data picture = picture[hearlength+4:] fhand = open(thispath, "wb") fhand.write(picture) fhand.close() # Code: http://www.py4e.com/code3/urljpeg.py # Or select Download from this trinket's left-hand menu else: LogInfo('下载失败,地址格式存在问题!')# 使用 pythonnet 的方式引入动态链接库import clr# 此处保证动态链接库文件放在当前文件夹中,如果不在应该使用这种方式# clr.AddReference('D:\\Path\\DotNetWithPython')# clr.AddReference('D:\\Path\\DotNetWithPython.dll')clr.AddReference('DotNetWithPython')from DotNetWithPython import *mainapp = MainForm()mainapp.DownloadEvent += Downloadmainapp.ShowDialog()


四、运行效果




五、存在问题

功能实现了,但是有一个无法解决的问题,就是文件开始下载时WinForm的界面会卡死。疑似主窗体不是用当前线程打开的,但不能解释为什么下载开始没有卡顿。有人能给我一些指导吗?感激不尽!



总结

关于Python集成C#实现通过接口操作下载文件的功能的这篇文章到此为止。关于Python接口操作下载文件的更多信息

0

精彩评论

暂无评论...
验证码 换一张
取 消