博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
给 string 增加一个 GetInputStream 扩展方法
阅读量:5794 次
发布时间:2019-06-18

本文共 9415 字,大约阅读时间需要 31 分钟。

有时候,我们需要读取一些数据,而不管这数据来源于磁盘上的数据文件,还是来源于网络上的数据。于是,就有了下面的 StringExtensions.cs:

1 using System;  2 using System.IO;  3 using System.Net;  4  5 namespace Skyiv  6 {
7 public static class StringExtensions 8 {
9 public static Stream GetInputStream(this string fileNameOrUri, string user = null, string password = null) 10 {
11 if (!Uri.IsWellFormedUriString(fileNameOrUri, UriKind.Absolute)) return File.OpenRead(fileNameOrUri); 12 var uri = new Uri(fileNameOrUri); 13 if (uri.Scheme == Uri.UriSchemeHttp || uri.Scheme == Uri.UriSchemeHttps) return uri.GetHttpStream(); 14 if (uri.Scheme == Uri.UriSchemeFtp) return uri.GetFtpStream(user, password); 15 if (uri.Scheme == Uri.UriSchemeFile) return uri.GetFileStream(); 16 throw new NotSupportedException("Notsupported uri scheme: " + uri.Scheme); 17 } 18 19 static Stream GetFtpStream(this Uri uri, string user = null, string password = null) 20 {
21 var ftp = (FtpWebRequest)WebRequest.Create(uri); 22 if (user != null && user != "anonymous" && user != "ftp") 23 ftp.Credentials = new NetworkCredential(user, password); 24 ftp.Method = WebRequestMethods.Ftp.DownloadFile; 25 return ((FtpWebResponse)ftp.GetResponse()).GetResponseStream(); 26 } 27 28 static Stream GetHttpStream(this Uri uri) 29 {
30 return ((HttpWebResponse)((HttpWebRequest)WebRequest.Create(uri)).GetResponse()).GetResponseStream(); 31 } 32 33 static Stream GetFileStream(this Uri uri) 34 {
35 return ((FileWebResponse)((FileWebRequest)WebRequest.Create(uri)).GetResponse()).GetResponseStream(); 36 } 37 } 38 }

上述程序中:

  1. 第 9 到 17 行的 GetInputStream 扩展方法返回来读取的数据的输入流。
  2. 第 11 行调用 Uri 类的静态方法 IsWellFormedUriString 判断是否从网络上读取数据。如不是,则直接调用 File 类的静态方法 OpenRead 得到输入流。
  3. 第 12 行构造一个 Uri 类的实例。
  4. 第 13 行处理输入使用 http 或者 https 协议的情况。
  5. 第 14 行处理输入使用 ftp 协议的情况。
  6. 第 15 行处理输入使用 file 协议的情况。
  7. 第 16 行处理其他协议,就是直接抛出一个 NotSupportedException 异常,表示我们只支持上述四种协议。
  8. 第 19 到 26 行的 GetFtpStream 扩展方法用于获得 FTP 服务器上发送的响应数据的输入流,可以是匿名的,也可以是非匿名的。
  9. 第 28 到 31 行的 GetHttpStream 扩展方法用于获得使用 http 或者 https 协议的网络流。
  10. 第 33 到 36 行的 GetFileStream 扩展方法用于获得使用 file 协议的本地磁盘文件系统的数据流。

下面就是测试用的 CopyTester.cs:

1 using System.IO;  2  3 namespace Skyiv.Test  4 {
5 static class CopyTester 6 {
7 static void Main(string[] args) 8 {
9 args[0].GetInputStream().CopyTo(File.Create(args[1])); 10 } 11 } 12 }

这个测试程序的功能就是拷贝数据,需要两个命令行参数:

  1. 第一个命令行参数指定数据来源,可以是本地磁盘文件,也可以是网络数据,支持 https、http、ftp 和 file 协议,当然,file 协议实际上还是读取本地磁盘文件。
  2. 第二个命令行参数指定将要拷贝到的本地磁盘文件的名称。

上述测试程序实质内容就是第 9 行的语句:

  1. 使用第一个命令行参数 args[0] 调用 String 类的 GetInputStream 扩展方法得到输入流。
  2. 调用 Stream 类的 CopyTo 方法将输入流拷贝到输出流。
  3. 输出流是使用 File 类的静态方法 Create 得到的。

在 Windows 操作系统中编译和运行:

E:\work> csc CopyTester.cs StringExtensions.csMicrosoft(R) Visual C# 2010 编译器 4.0.30319.1 版版权所有(C) Microsoft Corporation。保留所有权利。E:\work> CopyTester https://github.com/mono/xsp/zipball/master mono-xsp.zipE:\work> CopyTester http://mysql.ntu.edu.tw/Downloads/Connector-Net/mysql-connector-net-6.5.4-noinstall.zip mysql-connector.zipE:\work> CopyTester ftp://ftp.ntu.edu.tw/pub/MySQL/Downloads/Connector-Net/mysql-connector-net-6.5.4-noinstall.zip mysql-connector.2.zipE:\work> CopyTester file:///E:/work/mysql-connector.zip mysql-connector.3.zipE:\work> CopyTester mysql-connector.zip mysql-connector.4.zipE:\work> dir *.zip2012/03/11  09:35           468,024 mono-xsp.zip2012/03/11  09:37         4,176,361 mysql-connector.2.zip2012/03/11  09:38         4,176,361 mysql-connector.3.zip2012/03/11  09:38         4,176,361 mysql-connector.4.zip2012/03/11  09:36         4,176,361 mysql-connector.zip

上面分别测试了以 https、http、ftp、file 协议读取网络数据,以及从本地磁盘上读取数据。注意,file 协议实际上还是从本地磁盘读取数据。

在 Linux 操作系统中编译和运行:

ben@vbox:~/work> dmcs CopyTester.cs StringExtensions.csben@vbox:~/work> mono CopyTester.exe http://mysql.ntu.edu.tw/Downloads/Connector-Net/mysql-connector-net-6.5.4-noinstall.zip mysql-connector.zipben@vbox:~/work> mono CopyTester.exe ftp://ftp.ntu.edu.tw/pub/MySQL/Downloads/Connector-Net/mysql-connector-net-6.5.4-noinstall.zip mysql-connector.2.zipben@vbox:~/work> mono CopyTester.exe file:///home/ben/work/mysql-connector.zip mysql-connector.3.zipben@vbox:~/work> mono CopyTester.exe mysql-connector.zip mysql-connector.4.zipben@vbox:~/work> ls -l *.zip-rw-r--r-- 1 ben users 4176361 Mar 11 09:54 mysql-connector.2.zip-rw-r--r-- 1 ben users 4176361 Mar 11 10:01 mysql-connector.3.zip-rw-r--r-- 1 ben users 4176361 Mar 11 10:01 mysql-connector.4.zip-rw-r--r-- 1 ben users 4176361 Mar 11 09:53 mysql-connector.zip

在 Windows 操作系统可以正常读取网络上的 https 数据流,在 Linux 操作系统中会失败:

ben@vbox:~/work> mono CopyTester.exe https://github.com/mono/xsp/zipball/master mono-xsp.zipUnhandled Exception: System.Net.WebException: Error getting response stream (Write: The authentication or decryption has failed.): SendFailure ---> System.IO.IOException: The authentication or decryption has failed. ---> Mono.Security.Protocol.Tls.TlsException: Invalid certificate received from server. Error code: 0xffffffff800b010a  at Mono.Security.Protocol.Tls.Handshake.Client.TlsServerCertificate.validateCertificates (Mono.Security.X509.X509CertificateCollection certificates) [0x00000] in :0  at Mono.Security.Protocol.Tls.Handshake.Client.TlsServerCertificate.ProcessAsTls1 () [0x00000] in :0  at Mono.Security.Protocol.Tls.Handshake.HandshakeMessage.Process () [0x00000] in :0  at (wrapper remoting-invoke-with-check) Mono.Security.Protocol.Tls.Handshake.HandshakeMessage:Process ()  at Mono.Security.Protocol.Tls.ClientRecordProtocol.ProcessHandshakeMessage (Mono.Security.Protocol.Tls.TlsStream handMsg) [0x00000] in :0  at Mono.Security.Protocol.Tls.RecordProtocol.InternalReceiveRecordCallback (IAsyncResult asyncResult) [0x00000] in :0  --- End of inner exception stack trace ---  at Mono.Security.Protocol.Tls.SslStreamBase.AsyncHandshakeCallback (IAsyncResult asyncResult) [0x00000] in :0  --- End of inner exception stack trace ---  at System.Net.HttpWebRequest.EndGetResponse (IAsyncResult asyncResult) [0x00000] in :0  at System.Net.HttpWebRequest.GetResponse () [0x00000] in :0  at Skyiv.StringExtensions.GetHttpStream (System.Uri uri) [0x00000] in :0  at Skyiv.StringExtensions.GetInputStream (System.String fileNameOrUri, System.String user, System.String password) [0x00000] in :0  at Skyiv.Test.CopyTester.Main (System.String[] args) [0x00000] in :0[ERROR] FATAL UNHANDLED EXCEPTION: System.Net.WebException: Error getting response stream (Write: The authentication or decryption has failed.): SendFailure ---> System.IO.IOException: The authentication or decryption has failed. ---> Mono.Security.Protocol.Tls.TlsException: Invalid certificate received from server. Error code: 0xffffffff800b010a  at Mono.Security.Protocol.Tls.Handshake.Client.TlsServerCertificate.validateCertificates (Mono.Security.X509.X509CertificateCollection certificates) [0x00000] in :0  at Mono.Security.Protocol.Tls.Handshake.Client.TlsServerCertificate.ProcessAsTls1 () [0x00000] in :0  at Mono.Security.Protocol.Tls.Handshake.HandshakeMessage.Process () [0x00000] in :0  at (wrapper remoting-invoke-with-check) Mono.Security.Protocol.Tls.Handshake.HandshakeMessage:Process ()  at Mono.Security.Protocol.Tls.ClientRecordProtocol.ProcessHandshakeMessage (Mono.Security.Protocol.Tls.TlsStream handMsg) [0x00000] in :0  at Mono.Security.Protocol.Tls.RecordProtocol.InternalReceiveRecordCallback (IAsyncResult asyncResult) [0x00000] in :0  --- End of inner exception stack trace ---  at Mono.Security.Protocol.Tls.SslStreamBase.AsyncHandshakeCallback (IAsyncResult asyncResult) [0x00000] in :0  --- End of inner exception stack trace ---  at System.Net.HttpWebRequest.EndGetResponse (IAsyncResult asyncResult) [0x00000] in :0  at System.Net.HttpWebRequest.GetResponse () [0x00000] in :0  at Skyiv.StringExtensions.GetHttpStream (System.Uri uri) [0x00000] in :0  at Skyiv.StringExtensions.GetInputStream (System.String fileNameOrUri, System.String user, System.String password) [0x00000] in :0  at Skyiv.Test.CopyTester.Main (System.String[] args) [0x00000] in :0

不知道是不是我的 openSUSE 12.1 操作系统或者是 Mono 2.10.6 运行环境还需要进行一些配置,以便满足 https 的安全验证要求。但是我在 Windows 操作系统中也没有进行特别的配置。而且在 Linux 操作系统中使用 wget 命令也能够下载 https 协议的数据:

ben@vbox:~/work> wget https://github.com/mono/xsp/zipball/masterasking libproxy about url 'https://github.com/mono/xsp/zipball/master'libproxy suggest to use 'direct://'--2012-03-11 13:48:32--  https://github.com/mono/xsp/zipball/masterResolving github.com (github.com)... 207.97.227.239Connecting to github.com (github.com)|207.97.227.239|:443... connected.HTTP request sent, awaiting response... 302 FoundLocation: https://nodeload.github.com/mono/xsp/zipball/master [following]asking libproxy about url 'https://nodeload.github.com/mono/xsp/zipball/master'libproxy suggest to use 'direct://'--2012-03-11 13:48:34--  https://nodeload.github.com/mono/xsp/zipball/masterResolving nodeload.github.com (nodeload.github.com)... 207.97.227.252Connecting to nodeload.github.com (nodeload.github.com)|207.97.227.252|:443... connected.HTTP request sent, awaiting response... 200 OKLength: 468024 (457K) [application/octet-stream]Saving to: `master'100%[======================================>] 468,024  46.5K/s in 17s2012-03-11 13:48:54 (27.0 KB/s) - `master' saved [468024/468024]ben@vbox:~/work> mv master mono-xsp.zip

这样看来,Mono 环境的 HttpWebRequest 类可能需要进行一些设置才能读取 https 协议的数据。如果有哪位园友知道的话,请在本文的评论里告诉我,谢谢。

参考资料

转载地址:http://nnffx.baihongyu.com/

你可能感兴趣的文章
Apache® CarbonData™ 成 Apache 软件基金会顶级项目
查看>>
KaOS 2017.06 发布,桌面 Linux 发行版
查看>>
开发者技能修炼的 5 个等级,你冲关到哪儿了?
查看>>
OSC 职位推荐: Java 开发招聘专场
查看>>
基于 ThinkJS + React 的通用博客系统 Firekylin
查看>>
如何简单粗暴的编写属于自己的插件
查看>>
collectd 5.4.2/5.3.2 发布,系统监控和统计工具
查看>>
中国将在 2016 年启用量子通信网络
查看>>
《Unity开发实战》——2.1节引言
查看>>
《Hadoop集群与安全》一1.4 小结
查看>>
《PPT高手之道:六步变身职场幻灯派》一01 谋篇——以听众为中心
查看>>
《C++面向对象高效编程(第2版)》——2.23 Booch 表示法
查看>>
《用于物联网的Arduino项目开发:实用案例解析》—— 导读
查看>>
如何在 Fedora 工作站上截图
查看>>
《机器学习与数据科学(基于R的统计学习方法)》——2.9 从网站中抓取数据...
查看>>
《WebGL入门指南》——第2章,第2.3节一个简单的Three.js网页
查看>>
《乐在C语言》一2.3 关键词
查看>>
《jQuery、jQuery UI及jQuery Mobile技巧与示例》——7.3 技巧:将元素转换为按钮微件...
查看>>
腾讯Android自动化测试实战第2章
查看>>
Java核心技术卷I基础知识3.8.4 确定循环
查看>>