1. 程式人生 > >給 string 添加一個 GetInputStream 擴展方法

給 string 添加一個 GetInputStream 擴展方法

failed otsu suse fat valid ads filename work ase

有時候,我們須要讀取一些數據,而無論這數據來源於磁盤上的數據文件,還是來源於網絡上的數據。於是。就有了以下的 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 server上發送的響應數據的輸入流。能夠是匿名的。也能夠是非匿名的。
  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.cs
Microsoft(R) Visual C# 2010 編譯器 4.0.30319.1 版
版權全部(C) Microsoft Corporation。

保留全部權利。 E:\work> CopyTester https://github.com/mono/xsp/zipball/master mono-xsp.zip E:\work> CopyTester http://mysql.ntu.edu.tw/Downloads/Connector-Net/mysql-connector-net-6.5.4-noinstall.zip mysql-connector.zip E:\work> CopyTester ftp://ftp.ntu.edu.tw/pub/MySQL/Downloads/Connector-Net/mysql-connector-net-6.5.4-noinstall.zip mysql-connector.2.zip E:\work> CopyTester file:///E:/work/mysql-connector.zip mysql-connector.3.zip E:\work> CopyTester mysql-connector.zip mysql-connector.4.zip E:\work> dir *.zip 2012/03/11 09:35 468,024 mono-xsp.zip 2012/03/11 09:37 4,176,361 mysql-connector.2.zip 2012/03/11 09:38 4,176,361 mysql-connector.3.zip 2012/03/11 09:38 4,176,361 mysql-connector.4.zip 2012/03/11 09:36 4,176,361 mysql-connector.zip

上面分別測試了以 https、http、ftp、file 協議讀取網絡數據,以及從本地磁盤上讀取數據。註意。file 協議實際上還是從本地磁盤讀取數據。

在 Linux 操作系統中編譯和執行:

[email protected]:~/work> dmcs CopyTester.cs StringExtensions.cs
[email protected]:~/work> mono CopyTester.exe http://mysql.ntu.edu.tw/Downloads/Connector-Net/mysql-connector-net-6.5.4-noinstall.zip mysql-connector.zip
[email protected]:~/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.zip
[email protected]:~/work> mono CopyTester.exe file:///home/ben/work/mysql-connector.zip mysql-connector.3.zip
[email protected]:~/work> mono CopyTester.exe mysql-connector.zip mysql-connector.4.zip
[email protected]:~/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 操作系統中會失敗:

[email protected]:~/work> mono CopyTester.exe https://github.com/mono/xsp/zipball/master mono-xsp.zip

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
[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 協議的數據:

[email protected]:~/work> wget https://github.com/mono/xsp/zipball/master
asking 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/master
Resolving github.com (github.com)... 207.97.227.239
Connecting to github.com (github.com)|207.97.227.239|:443... connected.
HTTP request sent, awaiting response... 302 Found
Location: 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/master
Resolving nodeload.github.com (nodeload.github.com)... 207.97.227.252
Connecting to nodeload.github.com (nodeload.github.com)|207.97.227.252|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 468024 (457K) [application/octet-stream]
Saving to: `master‘

100%[======================================>] 468,024  46.5K/s in 17s

2012-03-11 13:48:54 (27.0 KB/s) - `master‘ saved [468024/468024]

[email protected]:~/work> mv master mono-xsp.zip

這樣看來。Mono 環境的 HttpWebRequest 類可能須要進行一些設置才幹讀取 https 協議的數據。

假設有哪位園友知道的話,請在本文的評論裏告訴我,謝謝。

給 string 添加一個 GetInputStream 擴展方法